From 0584253a040e5c00c53c2ddc1a32a1de0c880bda Mon Sep 17 00:00:00 2001 From: erlingrj Date: Sun, 8 Dec 2024 09:05:39 -0800 Subject: [PATCH 01/65] Have LFC generate a main function that can optionally be included in the build --- examples/flexpret/CMakeLists.txt | 2 +- examples/flexpret/main.c | 5 --- examples/riot/buildAll.sh | 5 +++ examples/riot/hello_lf/main.c | 5 --- examples/riot/hello_lf/run/build.sh | 4 +++ examples/zephyr/buildAll.sh | 7 ++++- examples/zephyr/hello_lf/CMakeLists.txt | 4 +-- examples/zephyr/hello_lf/main.c | 5 --- examples/zephyr/hello_lf/run/build.sh | 4 +++ examples/zephyr/hello_lf/src/HelloLF.lf | 4 +-- .../lflang/generator/uc/UcCmakeGenerator.kt | 4 ++- .../lflang/generator/uc/UcMainGenerator.kt | 31 +++++++------------ .../lflang/generator/uc/UcMakeGenerator.kt | 3 ++ .../generator/uc/UcStandaloneGenerator.kt | 12 ++++--- make/riot/riot.mk | 5 ++- 15 files changed, 51 insertions(+), 49 deletions(-) delete mode 100644 examples/flexpret/main.c delete mode 100644 examples/riot/hello_lf/main.c create mode 100755 examples/riot/hello_lf/run/build.sh delete mode 100644 examples/zephyr/hello_lf/main.c create mode 100755 examples/zephyr/hello_lf/run/build.sh diff --git a/examples/flexpret/CMakeLists.txt b/examples/flexpret/CMakeLists.txt index 9ad27675..2a264437 100644 --- a/examples/flexpret/CMakeLists.txt +++ b/examples/flexpret/CMakeLists.txt @@ -14,7 +14,7 @@ project(fp-lf) include(src-gen/Smoke/CMakeLists.txt) add_subdirectory(${REACTOR_UC_PATH}) -add_executable(fp-smoke main.c ${LFC_GEN_SOURCES}) +add_executable(fp-smoke ${LFC_GEN_MAIN} ${LFC_GEN_SOURCES}) target_link_libraries(fp-smoke PUBLIC reactor-uc) target_include_directories(fp-smoke PRIVATE ${LFC_GEN_INCLUDE_DIRS}) diff --git a/examples/flexpret/main.c b/examples/flexpret/main.c deleted file mode 100644 index 306ba3a2..00000000 --- a/examples/flexpret/main.c +++ /dev/null @@ -1,5 +0,0 @@ -#include "lf_main.h" - -int main() { - lf_start(); -} \ No newline at end of file diff --git a/examples/riot/buildAll.sh b/examples/riot/buildAll.sh index 2ca9484b..eb1cffb3 100755 --- a/examples/riot/buildAll.sh +++ b/examples/riot/buildAll.sh @@ -21,3 +21,8 @@ for board in "${BOARDS[@]}"; do popd done done + +# Build lf example +pushd hello_lf +run/build.sh +popd \ No newline at end of file diff --git a/examples/riot/hello_lf/main.c b/examples/riot/hello_lf/main.c deleted file mode 100644 index 306ba3a2..00000000 --- a/examples/riot/hello_lf/main.c +++ /dev/null @@ -1,5 +0,0 @@ -#include "lf_main.h" - -int main() { - lf_start(); -} \ No newline at end of file diff --git a/examples/riot/hello_lf/run/build.sh b/examples/riot/hello_lf/run/build.sh new file mode 100755 index 00000000..80602f55 --- /dev/null +++ b/examples/riot/hello_lf/run/build.sh @@ -0,0 +1,4 @@ +#!/bin/env bash + +${REACTOR_UC_PATH}/lfc/bin/lfc-dev src/HelloLF.lf +make all \ No newline at end of file diff --git a/examples/zephyr/buildAll.sh b/examples/zephyr/buildAll.sh index 24a9e95c..82926bb0 100755 --- a/examples/zephyr/buildAll.sh +++ b/examples/zephyr/buildAll.sh @@ -16,4 +16,9 @@ for dir in "${FOLDERS[@]}"; do pushd $dir $COMMAND popd -done \ No newline at end of file +done + +# Build lf example +pushd hello_lf +run/build.sh +popd \ No newline at end of file diff --git a/examples/zephyr/hello_lf/CMakeLists.txt b/examples/zephyr/hello_lf/CMakeLists.txt index 0fba35c1..39521f82 100644 --- a/examples/zephyr/hello_lf/CMakeLists.txt +++ b/examples/zephyr/hello_lf/CMakeLists.txt @@ -6,6 +6,6 @@ set(PLATFORM "ZEPHYR" CACHE STRING "Set platform to Zephyr") include(src-gen/HelloLF/CMakeLists.txt) add_subdirectory(${REACTOR_UC_PATH}) -target_sources(app PRIVATE main.c ${LF_SOURCES}) +target_sources(app PRIVATE ${LFC_GEN_MAIN} ${LFC_GEN_SOURCES}) target_link_libraries(app PRIVATE reactor-uc) -target_include_directories(app PRIVATE ${LF_INCLUDE_DIRS}) +target_include_directories(app PRIVATE ${LFC_GEN_INCLUDE_DIRS}) diff --git a/examples/zephyr/hello_lf/main.c b/examples/zephyr/hello_lf/main.c deleted file mode 100644 index 306ba3a2..00000000 --- a/examples/zephyr/hello_lf/main.c +++ /dev/null @@ -1,5 +0,0 @@ -#include "lf_main.h" - -int main() { - lf_start(); -} \ No newline at end of file diff --git a/examples/zephyr/hello_lf/run/build.sh b/examples/zephyr/hello_lf/run/build.sh new file mode 100755 index 00000000..bda655cf --- /dev/null +++ b/examples/zephyr/hello_lf/run/build.sh @@ -0,0 +1,4 @@ +#!/bin/env bash + +${REACTOR_UC_PATH}/lfc/bin/lfc-dev src/HelloLF.lf +west build -b qemu_cortex_m3 -p always \ No newline at end of file diff --git a/examples/zephyr/hello_lf/src/HelloLF.lf b/examples/zephyr/hello_lf/src/HelloLF.lf index a884a08c..3d40abce 100644 --- a/examples/zephyr/hello_lf/src/HelloLF.lf +++ b/examples/zephyr/hello_lf/src/HelloLF.lf @@ -1,6 +1,4 @@ -target uC { - platform: Zephyr -} +target uC main reactor { reaction(startup) {= diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index ea00a28f..dc5db574 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -17,6 +17,7 @@ import org.lflang.unreachable import org.lflang.util.FileUtil import java.nio.file.Path import java.time.LocalDateTime +import kotlin.io.path.name import kotlin.math.max class UcCmakeGenerator(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig) { @@ -38,8 +39,9 @@ class UcCmakeGenerator(private val main: Reactor, private val targetConfig: Targ |# an existing CMake project. | |set(LFC_GEN_SOURCES - ${" | "..sources.joinWithLn { "$S{CMAKE_CURRENT_LIST_DIR}/${it.toUnixString()}"}} + ${" | "..sources.filterNot{it.name == "lf_main.c"}.joinWithLn { "$S{CMAKE_CURRENT_LIST_DIR}/${it.toUnixString()}"}} |) + |set(LFC_GEN_MAIN "$S{CMAKE_CURRENT_LIST_DIR}/lf_main.c") |set(REACTOR_UC_PATH $S{CMAKE_CURRENT_LIST_DIR}/reactor-uc) |set(LFC_GEN_INCLUDE_DIRS $S{CMAKE_CURRENT_LIST_DIR}) |set(REACTION_QUEUE_SIZE ${main.getReactionQueueSize()} CACHE STRING "Size of the reaction queue") diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index d23f32f9..4b45066f 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -20,23 +20,6 @@ class UcMainGenerator( ) { private val ucParameterGenerator = UcParameterGenerator(main) - // For the default POSIX platform we generate a main function, this is only for simplifing - // quick testing. For real applications the code-generated sources must be included in an - // existing project. - fun generateMainFunction() = with(PrependOperator) { - if (targetConfig.get(PlatformProperty.INSTANCE).platform == PlatformType.Platform.NATIVE) { - """ - |// The following is to support convenient compilation of LF programs - |// targeting POSIX. For programs targeting embedded platforms a - |// main function is not generated. - |int main(int argc, char **argv) { - | lf_start(); - |} - """.trimMargin() - } else { - "" - } - } fun getDuration() = if (targetConfig.isSet(TimeOutProperty.INSTANCE)) targetConfig.get(TimeOutProperty.INSTANCE).toCCode() else "FOREVER" @@ -44,7 +27,7 @@ class UcMainGenerator( fun fast() = if(targetConfig.isSet(FastProperty.INSTANCE)) "true" else "false" - fun generateMainSource() = with(PrependOperator) { + fun generateStartSource() = with(PrependOperator) { """ |#include "reactor-uc/reactor-uc.h" |#include "${fileConfig.getReactorHeaderPath(main).toUnixString()}" @@ -63,11 +46,10 @@ class UcMainGenerator( | lf_environment.start(&lf_environment); | lf_exit(); |} - ${" |"..generateMainFunction()} """.trimMargin() } - fun generateMainHeader() = with(PrependOperator) { + fun generateStartHeader() = with(PrependOperator) { """ |#ifndef REACTOR_UC_LF_MAIN_H |#define REACTOR_UC_LF_MAIN_H @@ -78,4 +60,13 @@ class UcMainGenerator( | """.trimMargin() } + + fun generateMainSource() = with(PrependOperator) { + """ + |#include "lf_start.h" + |int main(void) { + | lf_start(); + |} + """.trimMargin() + } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt index 81ba03d7..7b73a834 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt @@ -11,15 +11,18 @@ import org.lflang.target.property.BuildTypeProperty import org.lflang.toUnixString import java.nio.file.Path import java.time.LocalDateTime +import kotlin.io.path.name import kotlin.math.max class UcMakeGenerator(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig) { private val S = '$' // a little trick to escape the dollar sign with $S fun generateMake(sources: List) = with(PrependOperator) { + val sources = sources.filterNot { it.name=="lf_main.c" } """ | # Makefile generated for ${fileConfig.name} |LFC_GEN_SOURCES = \ ${" | "..sources.joinWithLn { it.toUnixString() + if (it != sources.last()) " \\" else ""}} + |LFC_GEN_MAIN = lf_main.c |REACTION_QUEUE_SIZE = ${max(main.getReactionQueueSize(), 1)} |EVENT_QUEUE_SIZE = ${max(main.getEventQueueSize(), 1)} | diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt index 8eb094fb..4c161ca3 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt @@ -33,18 +33,20 @@ class UcStandaloneGenerator(generator: UcGenerator, val srcGenPath: Path) : // generate the main source file (containing main()) val mainGenerator = UcMainGenerator(mainReactor, generator.targetConfig, generator.fileConfig) + val startSourceFile = Paths.get("lf_start.c") + val startHeaderFile = Paths.get("lf_start.h") val mainSourceFile = Paths.get("lf_main.c") - val mainHeaderFile = Paths.get("lf_main.h") + val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateStartSource()) val mainCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateMainSource()) - ucSources.add(mainSourceFile) + ucSources.addAll(listOf(startSourceFile, mainSourceFile)) + codeMaps[srcGenPath.resolve(startSourceFile)] = startCodeMap codeMaps[srcGenPath.resolve(mainSourceFile)] = mainCodeMap - println("Path: $srcGenPath $srcGenPath") - + FileUtil.writeToFile(startCodeMap.generatedCode, srcGenPath.resolve(startSourceFile), true) FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) - FileUtil.writeToFile(mainGenerator.generateMainHeader(), srcGenPath.resolve(mainHeaderFile), true) + FileUtil.writeToFile(mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) val cmakeGenerator = UcCmakeGenerator(mainReactor, targetConfig, generator.fileConfig) val makeGenerator = UcMakeGenerator(mainReactor, targetConfig, generator.fileConfig) diff --git a/make/riot/riot.mk b/make/riot/riot.mk index a1040634..99b486a5 100644 --- a/make/riot/riot.mk +++ b/make/riot/riot.mk @@ -5,7 +5,10 @@ ifdef SRC_GEN_PATH include $(SRC_GEN_PATH)/Makefile # Include generated c files -SRC += $(patsubst %, $(SRC_GEN_PATH)/%, $(LFC_GEN_SOURCES)) main.c +SRC += $(patsubst %, $(SRC_GEN_PATH)/%, $(LFC_GEN_SOURCES)) + +# Include generated main file +SRC += $(SRC_GEN_PATH)/${LFC_GEN_MAIN} # Include generated h files CFLAGS += -I$(SRC_GEN_PATH) From 6b516971ef1c61f13b703ce102cf05ad6cce3acf Mon Sep 17 00:00:00 2001 From: erlingrj Date: Sun, 8 Dec 2024 09:12:50 -0800 Subject: [PATCH 02/65] CI --- .github/workflows/riot.yml | 3 +++ .github/workflows/zephyr.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/riot.yml b/.github/workflows/riot.yml index 01249c5b..bef2e229 100644 --- a/.github/workflows/riot.yml +++ b/.github/workflows/riot.yml @@ -21,6 +21,9 @@ jobs: - name: Install dependencies uses: ./.github/actions/riot + - name: Setup environment + run: source env.bash + - name: Build examples working-directory: ${{ github.workspace }}/examples/riot run: ./buildAll.sh diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml index e425cb1f..77462fd9 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -15,6 +15,9 @@ jobs: - name: Install dependencies uses: ./.github/actions/zephyr + + - name: Setup environment + run: source env.bash - name: Build examples working-directory: ${{ github.workspace }}/examples/zephyr From 55b308a64252ae20d2b0f987b4f7e965354d72c0 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Sun, 8 Dec 2024 09:17:48 -0800 Subject: [PATCH 03/65] CI --- .github/workflows/riot.yml | 4 +--- .github/workflows/zephyr.yml | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/riot.yml b/.github/workflows/riot.yml index bef2e229..d20a74dc 100644 --- a/.github/workflows/riot.yml +++ b/.github/workflows/riot.yml @@ -12,6 +12,7 @@ jobs: env: RIOT_VERSION: "2024.07" RIOTBASE: "/opt/riot-2024.07" + REACTOR_UC_BASE: ${{ github.workspace }} steps: - name: Checkout uses: actions/checkout@v3 @@ -21,9 +22,6 @@ jobs: - name: Install dependencies uses: ./.github/actions/riot - - name: Setup environment - run: source env.bash - - name: Build examples working-directory: ${{ github.workspace }}/examples/riot run: ./buildAll.sh diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml index 77462fd9..2e613a40 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -7,6 +7,8 @@ jobs: ci: name: Build examples runs-on: ubuntu-latest + env: + REACTOR_UC_BASE: ${{ github.workspace }} steps: - name: Checkout uses: actions/checkout@v3 @@ -15,9 +17,6 @@ jobs: - name: Install dependencies uses: ./.github/actions/zephyr - - - name: Setup environment - run: source env.bash - name: Build examples working-directory: ${{ github.workspace }}/examples/zephyr From a8973e8d2465b58abd370b555c671f6bf307d9c2 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Sun, 8 Dec 2024 09:22:06 -0800 Subject: [PATCH 04/65] CI --- .github/workflows/riot.yml | 2 +- .github/workflows/zephyr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/riot.yml b/.github/workflows/riot.yml index d20a74dc..7bddfef8 100644 --- a/.github/workflows/riot.yml +++ b/.github/workflows/riot.yml @@ -12,7 +12,7 @@ jobs: env: RIOT_VERSION: "2024.07" RIOTBASE: "/opt/riot-2024.07" - REACTOR_UC_BASE: ${{ github.workspace }} + REACTOR_UC_PATH: ${{ github.workspace }} steps: - name: Checkout uses: actions/checkout@v3 diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml index 2e613a40..f925c4fe 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -8,7 +8,7 @@ jobs: name: Build examples runs-on: ubuntu-latest env: - REACTOR_UC_BASE: ${{ github.workspace }} + REACTOR_UC_PATH: ${{ github.workspace }} steps: - name: Checkout uses: actions/checkout@v3 From cfda3bded9d561dfffe5b4ea2f6bd079ee9f1434 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Sun, 8 Dec 2024 09:26:10 -0800 Subject: [PATCH 05/65] Setup LFC in RIOT container --- .github/workflows/riot.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/riot.yml b/.github/workflows/riot.yml index 7bddfef8..98ebc052 100644 --- a/.github/workflows/riot.yml +++ b/.github/workflows/riot.yml @@ -19,9 +19,12 @@ jobs: with: submodules: recursive - - name: Install dependencies + - name: Install RIOT dependencies uses: ./.github/actions/riot + - name: Install LFC dependencies + uses: ./.github/actions/lingua-franca + - name: Build examples working-directory: ${{ github.workspace }}/examples/riot run: ./buildAll.sh From 2c38b983a170dc99237a39b1355cd2aac2b006ae Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Sun, 8 Dec 2024 20:47:37 +0100 Subject: [PATCH 06/65] Switch to riot master --- .github/actions/riot/action.yml | 6 ++++-- .github/workflows/riot.yml | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/actions/riot/action.yml b/.github/actions/riot/action.yml index bfbc0355..8e03684d 100644 --- a/.github/actions/riot/action.yml +++ b/.github/actions/riot/action.yml @@ -8,10 +8,12 @@ runs: uses: actions/cache@v4 with: path: ${{env.RIOTBASE}} - key: riot-${{env.RIOT_VERSION}} + key: riot-${{env.RIOT_COMMIT_ID}} - name: Install RIOT if: steps.cache-riot.outputs.cache-hit != 'true' run: | - git clone -b ${{env.RIOT_VERSION}}-branch https://github.com/RIOT-OS/RIOT.git ${{env.RIOTBASE}} + git clone -b https://github.com/RIOT-OS/RIOT.git ${{env.RIOTBASE}} + cd ${{env.RIOTBASE}} + git checkout ${{env.RIOT_COMMIT_ID}} shell: bash diff --git a/.github/workflows/riot.yml b/.github/workflows/riot.yml index 98ebc052..b0f7f0ad 100644 --- a/.github/workflows/riot.yml +++ b/.github/workflows/riot.yml @@ -10,8 +10,8 @@ jobs: container: image: riot/riotbuild:latest env: - RIOT_VERSION: "2024.07" - RIOTBASE: "/opt/riot-2024.07" + RIOT_COMMIT_ID: "cb5500b4b7600d95f42a3070abae402be4f63845" + RIOTBASE: "/opt/riot-cb5500b4b7600d95f42a3070abae402be4f63845" REACTOR_UC_PATH: ${{ github.workspace }} steps: - name: Checkout From 68e8d1160b2f392c987d17208999941b88d3198e Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Sun, 8 Dec 2024 20:51:07 +0100 Subject: [PATCH 07/65] Fix --- .github/actions/riot/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/riot/action.yml b/.github/actions/riot/action.yml index 8e03684d..be422310 100644 --- a/.github/actions/riot/action.yml +++ b/.github/actions/riot/action.yml @@ -13,7 +13,7 @@ runs: - name: Install RIOT if: steps.cache-riot.outputs.cache-hit != 'true' run: | - git clone -b https://github.com/RIOT-OS/RIOT.git ${{env.RIOTBASE}} + git clone https://github.com/RIOT-OS/RIOT.git ${{env.RIOTBASE}} cd ${{env.RIOTBASE}} git checkout ${{env.RIOT_COMMIT_ID}} shell: bash From 60ceeea602096b19b5539da0beff8bc7aa9c5f6c Mon Sep 17 00:00:00 2001 From: erlingrj Date: Sun, 8 Dec 2024 17:05:07 -0800 Subject: [PATCH 08/65] Install LFC deps in zephyr CI also --- .github/workflows/zephyr.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml index f925c4fe..3a3a35c9 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -15,9 +15,12 @@ jobs: with: submodules: recursive - - name: Install dependencies + - name: Install Zephyr dependencies uses: ./.github/actions/zephyr + - name: Install LFC dependencies + uses: ./.github/actions/lingua-franca + - name: Build examples working-directory: ${{ github.workspace }}/examples/zephyr run: ./buildAll.sh From 2704dbd63ae293ad2e0d62c892d5f3e0ad0b2bb8 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Sun, 8 Dec 2024 21:33:13 -0800 Subject: [PATCH 09/65] WIP --- .../posix/federated_lf/src/HelloFederated.lf | 25 +++++ .../generator/uc/UcFederateGenerator.kt | 27 ++--- .../org/lflang/generator/uc/UcFileConfig.kt | 6 +- .../org/lflang/generator/uc/UcGenerator.kt | 99 ++++++++++--------- 4 files changed, 94 insertions(+), 63 deletions(-) create mode 100644 examples/posix/federated_lf/src/HelloFederated.lf diff --git a/examples/posix/federated_lf/src/HelloFederated.lf b/examples/posix/federated_lf/src/HelloFederated.lf new file mode 100644 index 00000000..09f7ec7c --- /dev/null +++ b/examples/posix/federated_lf/src/HelloFederated.lf @@ -0,0 +1,25 @@ +target uC + +reactor Source { + output out: char[12] + + timer t(0, 1 sec) + + reaction(t) -> out {= + lf_set_array(out, "Hello World"); + =} +} + +reactor Sink { + input in: char[12] + + reaction(in) {= + printf("Received: %s\n" , in->value); + =} +} + +federated reactor { + src = new Source() + sink = new Sink() + src.out -> sink.in +} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 3e50e564..59a71af1 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -6,7 +6,7 @@ import org.lflang.lf.* import org.lflang.reactor import org.lflang.toUnixString -class UcFederateGenerator(private val federate: Instantiation, fileConfig: UcFileConfig, messageReporter: MessageReporter) { +class UcFederateGenerator(private val federate: Instantiation, private val fileConfig: UcFileConfig, messageReporter: MessageReporter) { private val container = federate.eContainer() as Reactor private val reactor = federate.reactor @@ -19,8 +19,9 @@ class UcFederateGenerator(private val federate: Instantiation, fileConfig: UcFil fun numBundles() = 1 - private val Reactor.codeType - get(): String = "federate_${federate.name}" + private val includeGuard = "LFC_GEN_FEDERATE_${federate.name.uppercase()}_H" + private val topLevelCodeType = "Federate_${federate.name}" + private fun generateFederateStruct() = with(PrependOperator) { """ @@ -28,17 +29,19 @@ class UcFederateGenerator(private val federate: Instantiation, fileConfig: UcFil | Reactor super; ${" | "..instances.generateReactorStructField(federate)} ${" | "..connections.generateReactorStructFields()} + ${" | "..instances.generateReactorStructContainedInputFields(federate)} + ${" | "..instances.generateReactorStructContainedOutputFields(federate)} | LF_FEDERATE_BOOKKEEPING_INSTANCES(${numBundles()}); - |} ${reactor.codeType}; + |} ${topLevelCodeType}; | """.trimMargin() } private fun generateCtorDefinition() = with(PrependOperator) { """ - |LF_REACTOR_CTOR_SIGNATURE(${reactor.codeType}) { + |LF_REACTOR_CTOR_SIGNATURE(${topLevelCodeType}) { | LF_FEDERATE_CTOR_PREAMBLE(); - | LF_REACTOR_CTOR(${reactor.codeType}); + | LF_REACTOR_CTOR(${topLevelCodeType}); ${" | "..instances.generateReactorCtorCode(federate)} ${" | "..connections.generateReactorCtorCodes()} |} @@ -48,14 +51,15 @@ class UcFederateGenerator(private val federate: Instantiation, fileConfig: UcFil fun generateHeader() = with(PrependOperator) { """ + |#ifndef ${includeGuard} + |#define ${includeGuard} |#include "reactor-uc/reactor-uc.h" + |#include "${fileConfig.getReactorHeaderPath(reactor).toUnixString()}" | - ${" |"..instances.generateIncludes()} - ${" |"..reactions.generateSelfStructs()} - ${" |"..ports.generateSelfStructs()} ${" |"..connections.generateSelfStructs()} + ${" |"..generateFederateStruct()} |//The reactor self struct - | + |#endif // ${includeGuard} """.trimMargin() } @@ -63,9 +67,6 @@ class UcFederateGenerator(private val federate: Instantiation, fileConfig: UcFil """ |#include "${headerFile}" | - ${" |"..reactions.generateReactionBodies()} - ${" |"..reactions.generateReactionCtors()} - ${" |"..ports.generateCtors()} ${" |"..connections.generateCtors()} ${" |"..generateCtorDefinition()} | diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt index 0fa246bc..88f1a5a2 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt @@ -24,16 +24,16 @@ class UcFileConfig(resource: Resource, srcGenBasePath: Path, useHierarchicalBin: /** Relative path to the directory where all source files for this resource should be generated in. */ private fun getGenDir(r: Resource): Path = this.getDirectory(r).resolve(r.name) - /** Path to the header file corresponding to this reactor */ - fun getReactorHeaderPath(r: Reactor): Path = getGenDir(r.eResource()).resolve("${r.name}.h") /** Path to the header file with preambles defined for this reactor */ fun getPreambleHeaderPath(r: Resource): Path = getGenDir(r).resolve("_lf_preamble.h") - /** Path to the source file corresponding to this reactor (needed for non generic reactors) */ fun getReactorSourcePath(r: Reactor): Path = getGenDir(r.eResource()).resolve("${r.name}.c") + /** Path to the header file corresponding to this reactor */ + fun getReactorHeaderPath(r: Reactor): Path = getGenDir(r.eResource()).resolve("${r.name}.h") + /** Path to the build directory containing CMake-generated files */ val buildPath: Path get() = this.outPath.resolve("build") } \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index 88bb2b26..67753dc6 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -1,6 +1,7 @@ package org.lflang.generator.uc import org.eclipse.emf.ecore.resource.Resource +import org.lflang.allInstantiations import org.lflang.generator.* import org.lflang.generator.GeneratorUtils.canGenerate import org.lflang.generator.LFGeneratorContext.Mode @@ -41,50 +42,32 @@ class UcGenerator( val res = mutableListOf() for (reactor in reactors) { if (reactor.isFederated) { - res.addAll(reactor.instantiations) + res.addAll(reactor.allInstantiations) } } return res } - fun doGenerateTopLevel(resource: Resource, context: LFGeneratorContext, srcGenPath: Path) { + private fun getAllInstantiatedReactors(top: Reactor): List { + val res = mutableListOf() + for (inst in top.allInstantiations) { + res.add(inst.reactor) + res.addAll(getAllInstantiatedReactors(inst.reactor)) + } + return res.distinct() + } + + fun doGenerateTopLevel(resource: Resource, context: LFGeneratorContext, srcGenPath: Path, isFederated: Boolean) { if (!canGenerate(errorsOccurred(), mainDef, messageReporter, context)) return // create a platform-specific generator val platformGenerator: UcPlatformGenerator = getPlatformGenerator(srcGenPath) // generate all core files - generateFiles(srcGenPath, getAllImportedResources(resource)) + generateFiles(mainDef, srcGenPath, getAllImportedResources(mainDef.eResource())) // generate platform specific files platformGenerator.generatePlatformFiles() - - // We only invoke CMake on the generated sources when we are targeting POSIX. If not - // it is the users responsibility to integrate this into the build system of the target - // platform. - if (platform.platform != PlatformType.Platform.NATIVE) { - println("Exiting before invoking target compiler.") - context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps)) - } else if (context.mode == Mode.LSP_MEDIUM) { - context.reportProgress( - "Code generation complete. Validating generated code...", IntegratedBuilder.GENERATED_PERCENT_PROGRESS - ) - if (platformGenerator.doCompile(context)) { - UcValidator(fileConfig, messageReporter, codeMaps).doValidate(context) - context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps)) - } else { - context.unsuccessfulFinish() - } - } else { - context.reportProgress( - "Code generation complete. Compiling...", IntegratedBuilder.GENERATED_PERCENT_PROGRESS - ) - if (platformGenerator.doCompile(context)) { - context.finish(GeneratorResult.Status.COMPILED, codeMaps) - } else { - context.unsuccessfulFinish() - } - } } @@ -93,12 +76,14 @@ class UcGenerator( val federates = getAllFederates(); if (federates.isEmpty()) { - doGenerateTopLevel(resource, context, fileConfig.srcGenPath) + doGenerateTopLevel(resource, context, fileConfig.srcGenPath, false) } else { for (federate in federates) { mainDef = federate - doGenerateTopLevel(resource, context, fileConfig.srcGenPath.resolve(federate.name)) + doGenerateTopLevel(resource, context, fileConfig.srcGenPath.resolve(federate.name), true) + } + context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps)) } } @@ -156,23 +141,43 @@ class UcGenerator( return resources } - private fun generateFiles(srcGenPath: Path, resources: Set) { + private fun generateReactorFiles(reactor: Reactor, srcGenPath: Path) { + val generator = UcReactorGenerator(reactor, fileConfig, messageReporter) + val headerFile = fileConfig.getReactorHeaderPath(reactor) + val sourceFile = fileConfig.getReactorSourcePath(reactor) + val reactorCodeMap = CodeMap.fromGeneratedCode(generator.generateSource()) + if (!reactor.isGeneric) + ucSources.add(sourceFile) + codeMaps[srcGenPath.resolve(sourceFile)] = reactorCodeMap + val headerCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) + codeMaps[srcGenPath.resolve(headerFile)] = headerCodeMap + + FileUtil.writeToFile(headerCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) + FileUtil.writeToFile(reactorCodeMap.generatedCode, srcGenPath.resolve(sourceFile), true) + } + + private fun generateFederateFiles(federate: Instantiation, srcGenPath: Path) { + val generator = UcFederateGenerator(federate, fileConfig, messageReporter) + + val headerFile = srcGenPath.resolve("Federate.h") + val sourceFile = srcGenPath.resolve("Federate.C") + val federateCodeMap = CodeMap.fromGeneratedCode(generator.generateSource()) + ucSources.add(sourceFile) + codeMaps[srcGenPath.resolve(sourceFile)] = federateCodeMap + val headerCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) + codeMaps[srcGenPath.resolve(headerFile)] = headerCodeMap + + FileUtil.writeToFile(headerCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) + FileUtil.writeToFile(federateCodeMap.generatedCode, srcGenPath.resolve(sourceFile), true) + } + + private fun generateFiles(mainDef: Instantiation, srcGenPath: Path, resources: Set) { // generate header and source files for all reactors - for (r in reactors.filterNot {it.isFederated}) { - val generator = UcReactorGenerator(r, fileConfig, messageReporter) - val headerFile = fileConfig.getReactorHeaderPath(r) - val sourceFile = fileConfig.getReactorSourcePath(r) - val reactorCodeMap = CodeMap.fromGeneratedCode(generator.generateSource()) - if (!r.isGeneric) - ucSources.add(sourceFile) - codeMaps[srcGenPath.resolve(sourceFile)] = reactorCodeMap - val headerCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) - codeMaps[srcGenPath.resolve(headerFile)] = headerCodeMap - - FileUtil.writeToFile(headerCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) - FileUtil.writeToFile(reactorCodeMap.generatedCode, srcGenPath.resolve(sourceFile), true) - } + getAllInstantiatedReactors(mainDef.reactor).map {generateReactorFiles(it, srcGenPath)} + if(mainDef.eContainer() is Reactor && (mainDef.eContainer() as Reactor).isFederated) { + generateFederateFiles(mainDef, srcGenPath) + } for (r in resources) { val generator = UcPreambleGenerator(r, fileConfig, scopeProvider) From 5ba13921af964701806089ad295aa6f7aef26a91 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 12 Dec 2024 15:50:47 -0800 Subject: [PATCH 10/65] WIP --- .../kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt | 2 +- lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 90811452..d0aa04b0 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -208,7 +208,7 @@ class UcConnectionGenerator(private val reactor: Reactor) { fun generateReactorCtorCodes() = ucConnections.connections.joinToString(prefix = "// Initialize connections\n", separator = "\n", postfix = "\n") { generateReactorCtorCode(it)} + - ucConnections.connections.joinToString(prefix = "// Initialize connections\n", separator = "\n", postfix = "\n") { generateConnectionStatements(it)} + ucConnections.connections.joinToString(prefix = "// Do connections \n", separator = "\n", postfix = "\n") { generateConnectionStatements(it)} fun generateCtors() = ucConnections.connections.joinToString(prefix = "// Connection constructors\n", separator = "\n", postfix = "\n"){ if(it.conn.isPhysical || it.conn.delay != null) generateDelayedCtor(it) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index 67753dc6..a0b090db 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -160,7 +160,7 @@ class UcGenerator( val generator = UcFederateGenerator(federate, fileConfig, messageReporter) val headerFile = srcGenPath.resolve("Federate.h") - val sourceFile = srcGenPath.resolve("Federate.C") + val sourceFile = srcGenPath.resolve("Federate.c") val federateCodeMap = CodeMap.fromGeneratedCode(generator.generateSource()) ucSources.add(sourceFile) codeMaps[srcGenPath.resolve(sourceFile)] = federateCodeMap From a8bc451c23ac0b5d1ee8658c0aec08037e43e97a Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 13 Dec 2024 11:18:37 -0800 Subject: [PATCH 11/65] WIP: SimpleFederated.lf now compiles. But invoking CMake correctly to generate two binaries and a shell script in `bin` is not working --- .../generator/uc/UcFederateGenerator.kt | 14 +-- .../org/lflang/generator/uc/UcGenerator.kt | 88 +++++++------------ .../generator/uc/UcInstanceGenerator.kt | 4 + .../lflang/generator/uc/UcMainGenerator.kt | 25 ++++++ .../generator/uc/UcPlatformGenerator.kt | 4 + .../generator/uc/UcStandaloneGenerator.kt | 8 +- test/lf/src/SimpleFederated.lf | 14 +++ 7 files changed, 93 insertions(+), 64 deletions(-) create mode 100644 test/lf/src/SimpleFederated.lf diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 59a71af1..f1a5d617 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -2,6 +2,7 @@ package org.lflang.generator.uc import org.lflang.MessageReporter import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate import org.lflang.lf.* import org.lflang.reactor import org.lflang.toUnixString @@ -15,12 +16,11 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC private val ports = UcPortGenerator(container, connections) private val reactions = UcReactionGenerator(container) private val instances = UcInstanceGenerator(container, parameters, ports, connections, reactions, fileConfig, messageReporter) - private val headerFile = fileConfig.getReactorHeaderPath(federate.reactor).toUnixString() + private val headerFile = "Federate.h" fun numBundles() = 1 private val includeGuard = "LFC_GEN_FEDERATE_${federate.name.uppercase()}_H" - private val topLevelCodeType = "Federate_${federate.name}" private fun generateFederateStruct() = with(PrependOperator) { @@ -32,16 +32,16 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC ${" | "..instances.generateReactorStructContainedInputFields(federate)} ${" | "..instances.generateReactorStructContainedOutputFields(federate)} | LF_FEDERATE_BOOKKEEPING_INSTANCES(${numBundles()}); - |} ${topLevelCodeType}; + |} ${federate.codeTypeFederate}; | """.trimMargin() } private fun generateCtorDefinition() = with(PrependOperator) { """ - |LF_REACTOR_CTOR_SIGNATURE(${topLevelCodeType}) { + |${generateCtorDeclaration()} { | LF_FEDERATE_CTOR_PREAMBLE(); - | LF_REACTOR_CTOR(${topLevelCodeType}); + | LF_REACTOR_CTOR(${federate.codeTypeFederate}); ${" | "..instances.generateReactorCtorCode(federate)} ${" | "..connections.generateReactorCtorCodes()} |} @@ -49,6 +49,8 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC """.trimMargin() } + private fun generateCtorDeclaration() = "LF_REACTOR_CTOR_SIGNATURE(${federate.codeTypeFederate})" + fun generateHeader() = with(PrependOperator) { """ |#ifndef ${includeGuard} @@ -58,7 +60,7 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC | ${" |"..connections.generateSelfStructs()} ${" |"..generateFederateStruct()} - |//The reactor self struct + ${" |"..generateCtorDeclaration()}; |#endif // ${includeGuard} """.trimMargin() } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index a0b090db..746c43d8 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -4,7 +4,7 @@ import org.eclipse.emf.ecore.resource.Resource import org.lflang.allInstantiations import org.lflang.generator.* import org.lflang.generator.GeneratorUtils.canGenerate -import org.lflang.generator.LFGeneratorContext.Mode +import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate import org.lflang.isGeneric import org.lflang.lf.Instantiation import org.lflang.lf.Reactor @@ -14,8 +14,6 @@ import org.lflang.target.Target import org.lflang.target.property.* import org.lflang.target.property.type.PlatformType import org.lflang.util.FileUtil -import java.io.IOException -import java.nio.file.Files import java.nio.file.Path @Suppress("unused") @@ -37,6 +35,8 @@ class UcGenerator( const val MINIMUM_CMAKE_VERSION = "3.5" } + + // Returns a possibly empty list of the federates in the current program private fun getAllFederates(): List { val res = mutableListOf() @@ -57,8 +57,8 @@ class UcGenerator( return res.distinct() } - fun doGenerateTopLevel(resource: Resource, context: LFGeneratorContext, srcGenPath: Path, isFederated: Boolean) { - if (!canGenerate(errorsOccurred(), mainDef, messageReporter, context)) return + fun doGenerateTopLevel(resource: Resource, context: LFGeneratorContext, srcGenPath: Path, isFederated: Boolean): GeneratorResult.Status { + if (!canGenerate(errorsOccurred(), mainDef, messageReporter, context)) return GeneratorResult.Status.FAILED // create a platform-specific generator val platformGenerator: UcPlatformGenerator = getPlatformGenerator(srcGenPath) @@ -68,6 +68,16 @@ class UcGenerator( // generate platform specific files platformGenerator.generatePlatformFiles() + + if (platform.platform == PlatformType.Platform.NATIVE) { + if (platformGenerator.doCompile(context)) { + return GeneratorResult.Status.COMPILED + } else { + return GeneratorResult.Status.FAILED + } + } else { + return GeneratorResult.Status.GENERATED + } } @@ -76,61 +86,27 @@ class UcGenerator( val federates = getAllFederates(); if (federates.isEmpty()) { - doGenerateTopLevel(resource, context, fileConfig.srcGenPath, false) + val res = doGenerateTopLevel(resource, context, fileConfig.srcGenPath, false) + if (res == GeneratorResult.Status.FAILED) { + context.unsuccessfulFinish() + return + } } else { for (federate in federates) { mainDef = federate - doGenerateTopLevel(resource, context, fileConfig.srcGenPath.resolve(federate.name), true) + val res = doGenerateTopLevel(resource, context, fileConfig.srcGenPath.resolve(federate.name), true) + if (res == GeneratorResult.Status.FAILED) { + context.unsuccessfulFinish() + return + } } - context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps)) } - - } - - /** - * Copy the contents of the entire src-gen directory to a nested src-gen directory next to the generated Dockerfile. - */ - private fun copySrcGenBaseDirIntoDockerDir() { - FileUtil.deleteDirectory(context.fileConfig.srcGenPath.resolve("src-gen")) - try { - // We need to copy in two steps via a temporary directory, as the target directory - // is located within the source directory. Without the temporary directory, copying - // fails as we modify the source while writing the target. - val tempDir = Files.createTempDirectory(context.fileConfig.outPath, "src-gen-directory") - try { - FileUtil.copyDirectoryContents(context.fileConfig.srcGenBasePath, tempDir, false) - FileUtil.copyDirectoryContents(tempDir, context.fileConfig.srcGenPath.resolve("src-gen"), false) - } catch (e: IOException) { - context.errorReporter.nowhere() - .error("Failed to copy sources to make them accessible to Docker: " + if (e.message == null) "No cause given" else e.message) - e.printStackTrace() - } finally { - FileUtil.deleteDirectory(tempDir) - } - if (errorsOccurred()) { - return - } - } catch (e: IOException) { - context.errorReporter.nowhere().error("Failed to create temporary directory.") - e.printStackTrace() - } - } - - private fun fetchReactorUc(version: String) { - val libPath = fileConfig.srcGenBasePath.resolve("reactor-uc") - // abort if the directory already exists - if (Files.isDirectory(libPath)) { - return + if (platform.platform == PlatformType.Platform.NATIVE) { + context.finish(GeneratorResult.Status.COMPILED, codeMaps) + } else { + context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps)) } - // clone the reactor-cpp repo and fetch the specified version - Files.createDirectories(libPath) - commandFactory.createCommand( - "git", - listOf("clone", "-n", "https://github.com/lf-lang/reactor-uc.git", "reactor-uc-$version"), - fileConfig.srcGenBasePath - ).run() - commandFactory.createCommand("git", listOf("checkout", version), libPath).run() } private fun getAllImportedResources(resource: Resource): Set { @@ -157,6 +133,10 @@ class UcGenerator( } private fun generateFederateFiles(federate: Instantiation, srcGenPath: Path) { + // First thing is that we need to also generate the top-level federate reactor files + generateReactorFiles(federate.reactor, srcGenPath) + + // Then we generate a reactor which wraps around the top-level reactor in the federate. val generator = UcFederateGenerator(federate, fileConfig, messageReporter) val headerFile = srcGenPath.resolve("Federate.h") @@ -175,7 +155,7 @@ class UcGenerator( // generate header and source files for all reactors getAllInstantiatedReactors(mainDef.reactor).map {generateReactorFiles(it, srcGenPath)} - if(mainDef.eContainer() is Reactor && (mainDef.eContainer() as Reactor).isFederated) { + if (mainDef.isAFederate) { generateFederateFiles(mainDef, srcGenPath) } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt index eff5925f..99789816 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt @@ -19,6 +19,10 @@ class UcInstanceGenerator( companion object { val Instantiation.width get(): Int = widthSpec?.getWidth()?:1 + val Instantiation.codeTypeFederate + get(): String = "Federate_${name}" + val Instantiation.isAFederate + get(): Boolean = this.eContainer() is Reactor && (this.eContainer() as Reactor).isFederated } fun generateIncludes(): String = diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index 4b45066f..43b8a090 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -2,8 +2,10 @@ package org.lflang.generator.uc import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.inferredType +import org.lflang.lf.Instantiation import org.lflang.lf.Parameter import org.lflang.lf.Reactor import org.lflang.target.property.FastProperty @@ -15,6 +17,7 @@ import org.lflang.toUnixString class UcMainGenerator( private val main: Reactor, + private val mainDef: Instantiation, private val targetConfig: TargetConfig, private val fileConfig: UcFileConfig, ) { @@ -49,6 +52,28 @@ class UcMainGenerator( """.trimMargin() } + fun generateFederatedStartSource() = with(PrependOperator) { + """ + |#include "reactor-uc/reactor-uc.h" + |#include "Federate.h" + |static ${mainDef.codeTypeFederate} main_reactor; + |static Environment lf_environment; + |void lf_exit(void) { + | Environment_free(&lf_environment); + |} + |void lf_start(void) { + | Environment_ctor(&lf_environment, (Reactor *)&main_reactor); + | ${mainDef.codeTypeFederate}_ctor(&main_reactor, NULL, &lf_environment); + | lf_environment.scheduler->duration = ${getDuration()}; + | lf_environment.scheduler->keep_alive = ${keepAlive()}; + | lf_environment.fast_mode = ${fast()}; + | lf_environment.assemble(&lf_environment); + | lf_environment.start(&lf_environment); + | lf_exit(); + |} + """.trimMargin() + } + fun generateStartHeader() = with(PrependOperator) { """ |#ifndef REACTOR_UC_LF_MAIN_H diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt index 2bde831d..eca6d0a8 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt @@ -4,6 +4,8 @@ import org.lflang.MessageReporter import org.lflang.target.TargetConfig import org.lflang.generator.GeneratorCommandFactory import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate +import org.lflang.reactor import org.lflang.target.property.BuildTypeProperty import org.lflang.toDefinition import org.lflang.toUnixString @@ -18,6 +20,8 @@ abstract class UcPlatformGenerator(protected val generator: UcGenerator) { protected val targetConfig: TargetConfig = generator.targetConfig protected val commandFactory: GeneratorCommandFactory = generator.commandFactory protected val mainReactor = generator.mainDef.reactorClass.toDefinition() + protected val isFederated = generator.mainDef.isAFederate + protected val relativeBinDir = fileConfig.outPath.relativize(fileConfig.binPath).toUnixString() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt index 4c161ca3..99f7ed58 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt @@ -31,13 +31,14 @@ class UcStandaloneGenerator(generator: UcGenerator, val srcGenPath: Path) : } val runtimePath: Path = Paths.get(reactorUCEnvPath) // generate the main source file (containing main()) - val mainGenerator = UcMainGenerator(mainReactor, generator.targetConfig, generator.fileConfig) + val mainGenerator = UcMainGenerator(mainReactor, generator.mainDef, generator.targetConfig, generator.fileConfig) val startSourceFile = Paths.get("lf_start.c") val startHeaderFile = Paths.get("lf_start.h") val mainSourceFile = Paths.get("lf_main.c") - val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateStartSource()) + val startCode = if (isFederated) mainGenerator.generateFederatedStartSource() else mainGenerator.generateStartSource() + val startCodeMap = CodeMap.fromGeneratedCode(startCode) val mainCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateMainSource()) ucSources.addAll(listOf(startSourceFile, mainSourceFile)) @@ -50,7 +51,6 @@ class UcStandaloneGenerator(generator: UcGenerator, val srcGenPath: Path) : val cmakeGenerator = UcCmakeGenerator(mainReactor, targetConfig, generator.fileConfig) val makeGenerator = UcMakeGenerator(mainReactor, targetConfig, generator.fileConfig) - val pkgName = fileConfig.srcGenPkgPath.fileName.toString() FileUtil.writeToFile(cmakeGenerator.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); try { @@ -177,7 +177,7 @@ class UcStandaloneGenerator(generator: UcGenerator, val srcGenPath: Path) : "-DCMAKE_INSTALL_BINDIR=$relativeBinDir", "--fresh", "-S", - sourcesRoot ?: fileConfig.srcGenPath.toUnixString(), + sourcesRoot ?: srcGenPath.toUnixString(), "-B", buildPath.fileName.toString() ) diff --git a/test/lf/src/SimpleFederated.lf b/test/lf/src/SimpleFederated.lf new file mode 100644 index 00000000..1515ea05 --- /dev/null +++ b/test/lf/src/SimpleFederated.lf @@ -0,0 +1,14 @@ +target uC { + platform: Native +} + +reactor R(id: int = 0) { + reaction(startup) {= + printf("Hello from Federate %d!\n", self->id); + =} +} + +federated reactor { + r1 = new R(id=1) + r2 = new R(id=2) +} \ No newline at end of file From ead4a67b0c7f6bedda2eada725d1b5d7918d96b9 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 13 Dec 2024 15:28:55 -0800 Subject: [PATCH 12/65] Generate a launch script for federated native --- .../lflang/generator/uc/UcCmakeGenerator.kt | 17 +- .../uc/UcFederatedLaunchScriptGenerator.kt | 50 +++++ .../uc/UcFederatedPlatformGenerator.kt | 202 ++++++++++++++++++ .../org/lflang/generator/uc/UcFileConfig.kt | 9 + .../org/lflang/generator/uc/UcGenerator.kt | 15 +- .../generator/uc/UcInstanceGenerator.kt | 2 +- .../generator/uc/UcPlatformGenerator.kt | 1 - ...or.kt => UcStandalonePlatformGenerator.kt} | 7 +- test/lf/src/failing/SimpleFederated.lf | 20 -- 9 files changed, 281 insertions(+), 42 deletions(-) create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt rename lfc/core/src/main/kotlin/org/lflang/generator/uc/{UcStandaloneGenerator.kt => UcStandalonePlatformGenerator.kt} (95%) delete mode 100644 test/lf/src/failing/SimpleFederated.lf diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index dc5db574..0d9a10ae 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -1,29 +1,30 @@ package org.lflang.generator.uc -import org.lflang.FileConfig +import org.lflang.* import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate import org.lflang.generator.uc.UcReactorGenerator.Companion.getEventQueueSize import org.lflang.generator.uc.UcReactorGenerator.Companion.getReactionQueueSize -import org.lflang.joinWithLn +import org.lflang.lf.Instantiation import org.lflang.lf.Reactor import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.CmakeIncludeProperty import org.lflang.target.property.PlatformProperty import org.lflang.target.property.type.PlatformType -import org.lflang.toUnixString -import org.lflang.unreachable import org.lflang.util.FileUtil import java.nio.file.Path import java.time.LocalDateTime import kotlin.io.path.name import kotlin.math.max -class UcCmakeGenerator(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig) { +class UcCmakeGenerator(private val mainDef: Instantiation, private val targetConfig: TargetConfig, private val fileConfig: FileConfig) { private val S = '$' // a little trick to escape the dollar sign with $S private val platform = targetConfig.get(PlatformProperty.INSTANCE).platform - val includeFiles = targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { fileConfig.srcPath.resolve(it).toUnixString() } + private val includeFiles = targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { fileConfig.srcPath.resolve(it).toUnixString() } + private val main = mainDef.reactor + private val mainTarget = if (mainDef.isAFederate) "${fileConfig.name}_${mainDef.name}" else fileConfig.name fun generateCmake(sources: List) = @@ -53,13 +54,13 @@ class UcCmakeGenerator(private val main: Reactor, private val targetConfig: Targ fun generateCmakePosix(sources: List) = with(PrependOperator) { """ |cmake_minimum_required(VERSION 3.5) - |project(${fileConfig.name} VERSION 0.0.0 LANGUAGES C) + |project(${mainTarget} LANGUAGES C) |set(PLATFORM POSIX CACHE STRING "Target platform") |set(REACTION_QUEUE_SIZE ${max(main.getReactionQueueSize(), 1)} CACHE STRING "Size of the reaction queue") |set(EVENT_QUEUE_SIZE ${max(main.getEventQueueSize(), 1)} CACHE STRING "Size of the event queue") |set(CMAKE_BUILD_TYPE ${targetConfig.getOrDefault(BuildTypeProperty.INSTANCE)}) | - |set(LF_MAIN_TARGET ${fileConfig.name}) + |set(LF_MAIN_TARGET ${mainTarget}) |set(SOURCES ${" | "..sources.joinWithLn { it.toUnixString() }} |) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt new file mode 100644 index 00000000..82fe2a7b --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt @@ -0,0 +1,50 @@ +package org.lflang.generator.uc + +import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate +import org.lflang.joinWithLn +import org.lflang.lf.Instantiation +import org.lflang.toUnixString +import kotlin.io.path.name + +class UcFederatedLaunchScriptGenerator(private val fileConfig: UcFileConfig) { + private val S = '$' // a little trick to escape the dollar sign with $S + fun generateLaunchScript(federates: List): String = with(PrependOperator) { + """ |#!/bin/env bash + | + |set -m + |shopt -s huponexit + |cleanup() { + | if [ "$S{EXITED_SUCCESSFULLY}" ] ; then + | exit 0 + | else + | printf "Killing federate %s.\n" $S{pids[*]} + | # The || true clause means this is not an error if kill fails. + | kill $S{pids[@]} || true + | exit 1 + | fi + |} + | + |trap 'cleanup; exit' EXIT + | + |# Launch all federates + |pids=() + ${" |"..federates.joinWithLn { launchFederate(it) }} + | + |# Wait for all federates to finish + |for pid in "$S{pids[@]}" + |do + | wait $S{pid} || exit $S? + |done + |EXITED_SUCCESSFULLY=true + """.trimMargin() + } + + fun launchFederate(federate: Instantiation) = with(PrependOperator) { + """ |echo "#### Launching federate ${federate.codeTypeFederate}" + |${fileConfig.binPath}/${federate.codeTypeFederate} + |pids+=$S! + | + """.trimMargin() + } +} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt new file mode 100644 index 00000000..6386ad39 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt @@ -0,0 +1,202 @@ +package org.lflang.generator.uc + +import org.lflang.generator.CodeMap +import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate +import org.lflang.target.property.BuildTypeProperty +import org.lflang.target.property.type.BuildTypeType.BuildType +import org.lflang.toUnixString +import org.lflang.util.FileUtil +import org.lflang.util.LFCommand +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.createSymbolicLinkPointingTo + +class UcFederatedPlatformGenerator(generator: UcGenerator, val srcGenPath: Path) : + UcPlatformGenerator(generator) { + + companion object { + fun buildTypeToCmakeConfig(type: BuildType) = when (type) { + BuildType.TEST -> "Debug" + else -> type.toString() + } + } + + override fun generatePlatformFiles() { + val reactorUCEnvPath = System.getenv("REACTOR_UC_PATH") + // FIXME: Improve this error handling + if (reactorUCEnvPath == null) { + messageReporter.nowhere().error("REACTOR_UC_PATH environment variable not defined. Do source env.bash in reactor-uc") + return; + } + val runtimePath: Path = Paths.get(reactorUCEnvPath) + val mainGenerator = UcMainGenerator(mainReactor, generator.mainDef, generator.targetConfig, generator.fileConfig) + + val startSourceFile = Paths.get("lf_start.c") + val startHeaderFile = Paths.get("lf_start.h") + val mainSourceFile = Paths.get("lf_main.c") + + val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateFederatedStartSource()) + val mainCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateMainSource()) + + ucSources.addAll(listOf(startSourceFile, mainSourceFile)) + codeMaps[srcGenPath.resolve(startSourceFile)] = startCodeMap + codeMaps[srcGenPath.resolve(mainSourceFile)] = mainCodeMap + + FileUtil.writeToFile(startCodeMap.generatedCode, srcGenPath.resolve(startSourceFile), true) + FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) + FileUtil.writeToFile(mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) + + val cmakeGenerator = UcCmakeGenerator(generator.mainDef, targetConfig, generator.fileConfig) + val makeGenerator = UcMakeGenerator(mainReactor, targetConfig, generator.fileConfig) + FileUtil.writeToFile(cmakeGenerator.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) + + val launchScriptGenerator = UcFederatedLaunchScriptGenerator(fileConfig) + FileUtil.writeToFile(launchScriptGenerator.generateLaunchScript(generator.getAllFederates()), fileConfig.binPath.resolve(fileConfig.name)) + + val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); + try { + runtimeSymlinkPath.createSymbolicLinkPointingTo(runtimePath); + } catch (e: Exception) { + // Do nothing + } + + FileUtil.writeToFile(makeGenerator.generateMake(ucSources), srcGenPath.resolve("Makefile"), true) + } + + override fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean): Boolean { + + // make sure the build directory exists + Files.createDirectories(fileConfig.buildPath) + + val version = checkCmakeVersion() + var parallelize = true + if (version != null && version.compareVersion("3.12.0") < 0) { + messageReporter.nowhere().warning("CMAKE is older than version 3.12. Parallel building is not supported.") + parallelize = false + } + + if (version != null) { + val cmakeReturnCode = runCmake(context) + + if (cmakeReturnCode == 0 && !onlyGenerateBuildFiles) { + // If cmake succeeded, run make + val makeCommand = createMakeCommand(fileConfig.buildPath, parallelize, generator.mainDef.codeTypeFederate) + val makeReturnCode = UcValidator(fileConfig, messageReporter, codeMaps).run(makeCommand, context.cancelIndicator) + var installReturnCode = 0 + if (makeReturnCode == 0) { + val installCommand = createMakeCommand(fileConfig.buildPath, parallelize, "install") + installReturnCode = installCommand.run(context.cancelIndicator) + if (installReturnCode == 0) { + println("SUCCESS (compiling generated C code)") + println("Generated source code is in ${fileConfig.srcGenPath}") + println("Compiled binary is in ${fileConfig.binPath}") + } + } + if ((makeReturnCode != 0 || installReturnCode != 0) && !messageReporter.errorsOccurred) { + // If errors occurred but none were reported, then the following message is the best we can do. + messageReporter.nowhere().error("make failed with error code $makeReturnCode") + } + } + if (cmakeReturnCode != 0) { + messageReporter.nowhere().error("cmake failed with error code $cmakeReturnCode") + } + } + return !messageReporter.errorsOccurred + } + + + private fun checkCmakeVersion(): String? { + // get the installed cmake version and make sure it is at least 3.5 + val cmd = commandFactory.createCommand("cmake", listOf("--version"), fileConfig.buildPath) + var version: String? = null + if (cmd != null && cmd.run() == 0) { + val regex = "\\d+(\\.\\d+)+".toRegex() + version = regex.find(cmd.output.toString())?.value + } + if (version == null || version.compareVersion("3.5.0") < 0) { + messageReporter.nowhere( + ).error( + "The uC target requires CMAKE >= 3.5.0 to compile the generated code. " + + "Auto-compiling can be disabled using the \"no-compile: true\" target property." + ) + return null + } + + return version + } + + + /** + * Run CMake to generate build files. + * @return True, if cmake run successfully + */ + private fun runCmake(context: LFGeneratorContext): Int { + val cmakeCommand = createCmakeCommand(fileConfig.buildPath, fileConfig.outPath) + return cmakeCommand.run(context.cancelIndicator) + } + + private fun String.compareVersion(other: String): Int { + val a = this.split(".").map { it.toInt() } + val b = other.split(".").map { it.toInt() } + for (x in (a zip b)) { + val res = x.first.compareTo(x.second) + if (res != 0) + return res + } + return 0 + } + + private fun getMakeArgs(buildPath: Path, parallelize: Boolean, target: String): List { + val cmakeConfig = buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE)) + val makeArgs = mutableListOf( + "--build", + buildPath.fileName.toString(), + "--config", + cmakeConfig, + "--target", + target + ) + + if (parallelize) { + makeArgs.addAll(listOf("--parallel", Runtime.getRuntime().availableProcessors().toString())) + } + + return makeArgs + } + + + private fun createMakeCommand(buildPath: Path, parallelize: Boolean, target: String): LFCommand { + val makeArgs = getMakeArgs(buildPath, parallelize, target) + return commandFactory.createCommand("cmake", makeArgs, buildPath.parent) + } + + private fun getCmakeArgs( + buildPath: Path, + outPath: Path, + sourcesRoot: String? = null + ) = cmakeArgs + listOf( + "-DCMAKE_INSTALL_PREFIX=${outPath.toUnixString()}", + "-DCMAKE_INSTALL_BINDIR=$relativeBinDir", + "--fresh", + "-S", + sourcesRoot ?: srcGenPath.toUnixString(), + "-B", + buildPath.fileName.toString() + ) + + private fun createCmakeCommand( + buildPath: Path, + outPath: Path, + sourcesRoot: String? = null + ): LFCommand { + val cmd = commandFactory.createCommand( + "cmake", + getCmakeArgs(buildPath, outPath, sourcesRoot), + buildPath.parent + ) + return cmd + } +} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt index 88f1a5a2..ba01f19e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt @@ -2,6 +2,8 @@ package org.lflang.generator.uc import org.eclipse.emf.ecore.resource.Resource import org.lflang.FileConfig +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate +import org.lflang.lf.Instantiation import org.lflang.lf.Reactor import org.lflang.name import org.lflang.util.FileUtil @@ -34,6 +36,13 @@ class UcFileConfig(resource: Resource, srcGenBasePath: Path, useHierarchicalBin: /** Path to the header file corresponding to this reactor */ fun getReactorHeaderPath(r: Reactor): Path = getGenDir(r.eResource()).resolve("${r.name}.h") + /** Path to the source file corresponding to this reactor (needed for non generic reactors) */ + fun getFederateSourcePath(f: Instantiation): Path = getGenDir(f.eResource()).resolve("${f.codeTypeFederate}.c") + + /** Path to the header file corresponding to this reactor */ + fun getFederateHeaderPath(f: Instantiation): Path = getGenDir(f.eResource()).resolve("${f.codeTypeFederate}.h") + + /** Path to the build directory containing CMake-generated files */ val buildPath: Path get() = this.outPath.resolve("build") } \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index 746c43d8..45556edd 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -38,7 +38,7 @@ class UcGenerator( // Returns a possibly empty list of the federates in the current program - private fun getAllFederates(): List { + fun getAllFederates(): List { val res = mutableListOf() for (reactor in reactors) { if (reactor.isFederated) { @@ -57,11 +57,9 @@ class UcGenerator( return res.distinct() } - fun doGenerateTopLevel(resource: Resource, context: LFGeneratorContext, srcGenPath: Path, isFederated: Boolean): GeneratorResult.Status { + fun doGenerateTopLevel(resource: Resource, context: LFGeneratorContext, srcGenPath: Path, platformGenerator: UcPlatformGenerator): GeneratorResult.Status { if (!canGenerate(errorsOccurred(), mainDef, messageReporter, context)) return GeneratorResult.Status.FAILED - // create a platform-specific generator - val platformGenerator: UcPlatformGenerator = getPlatformGenerator(srcGenPath) // generate all core files generateFiles(mainDef, srcGenPath, getAllImportedResources(mainDef.eResource())) @@ -85,8 +83,9 @@ class UcGenerator( super.doGenerate(resource, context) val federates = getAllFederates(); + // create a platform-specific generator if (federates.isEmpty()) { - val res = doGenerateTopLevel(resource, context, fileConfig.srcGenPath, false) + val res = doGenerateTopLevel(resource, context, fileConfig.srcGenPath, UcStandalonePlatformGenerator(this, fileConfig.srcGenPath)) if (res == GeneratorResult.Status.FAILED) { context.unsuccessfulFinish() return @@ -94,7 +93,9 @@ class UcGenerator( } else { for (federate in federates) { mainDef = federate - val res = doGenerateTopLevel(resource, context, fileConfig.srcGenPath.resolve(federate.name), true) + codeMaps.clear() + val srcGenPath = fileConfig.srcGenPath.resolve(federate.name) + val res = doGenerateTopLevel(resource, context, srcGenPath, UcFederatedPlatformGenerator(this, srcGenPath)) if (res == GeneratorResult.Status.FAILED) { context.unsuccessfulFinish() @@ -168,8 +169,6 @@ class UcGenerator( } } - private fun getPlatformGenerator(srcGenPath: Path) = UcStandaloneGenerator(this, srcGenPath) - override fun getTarget() = Target.UC override fun getTargetTypes(): TargetTypes = UcTypes diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt index 99789816..aefc91a0 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt @@ -20,7 +20,7 @@ class UcInstanceGenerator( val Instantiation.width get(): Int = widthSpec?.getWidth()?:1 val Instantiation.codeTypeFederate - get(): String = "Federate_${name}" + get(): String = "${(eContainer() as Reactor).name}_${name}" val Instantiation.isAFederate get(): Boolean = this.eContainer() is Reactor && (this.eContainer() as Reactor).isFederated } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt index eca6d0a8..b5e2e837 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt @@ -20,7 +20,6 @@ abstract class UcPlatformGenerator(protected val generator: UcGenerator) { protected val targetConfig: TargetConfig = generator.targetConfig protected val commandFactory: GeneratorCommandFactory = generator.commandFactory protected val mainReactor = generator.mainDef.reactorClass.toDefinition() - protected val isFederated = generator.mainDef.isAFederate protected val relativeBinDir = fileConfig.outPath.relativize(fileConfig.binPath).toUnixString() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt similarity index 95% rename from lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt rename to lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt index 99f7ed58..3fb1b9e4 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandaloneGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt @@ -12,7 +12,7 @@ import java.nio.file.Path import java.nio.file.Paths import kotlin.io.path.createSymbolicLinkPointingTo -class UcStandaloneGenerator(generator: UcGenerator, val srcGenPath: Path) : +class UcStandalonePlatformGenerator(generator: UcGenerator, val srcGenPath: Path) : UcPlatformGenerator(generator) { companion object { @@ -37,8 +37,7 @@ class UcStandaloneGenerator(generator: UcGenerator, val srcGenPath: Path) : val startHeaderFile = Paths.get("lf_start.h") val mainSourceFile = Paths.get("lf_main.c") - val startCode = if (isFederated) mainGenerator.generateFederatedStartSource() else mainGenerator.generateStartSource() - val startCodeMap = CodeMap.fromGeneratedCode(startCode) + val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateStartSource()) val mainCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateMainSource()) ucSources.addAll(listOf(startSourceFile, mainSourceFile)) @@ -49,7 +48,7 @@ class UcStandaloneGenerator(generator: UcGenerator, val srcGenPath: Path) : FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) FileUtil.writeToFile(mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) - val cmakeGenerator = UcCmakeGenerator(mainReactor, targetConfig, generator.fileConfig) + val cmakeGenerator = UcCmakeGenerator(generator.mainDef, targetConfig, generator.fileConfig) val makeGenerator = UcMakeGenerator(mainReactor, targetConfig, generator.fileConfig) FileUtil.writeToFile(cmakeGenerator.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); diff --git a/test/lf/src/failing/SimpleFederated.lf b/test/lf/src/failing/SimpleFederated.lf deleted file mode 100644 index 9f68f4b9..00000000 --- a/test/lf/src/failing/SimpleFederated.lf +++ /dev/null @@ -1,20 +0,0 @@ -target uC - - -reactor X { - reaction(startup) {= - printf("Hello from LF!\n"); - =} -} - -reactor R { - reaction(startup) {= - printf("Hello from LF!\n"); - =} - x = new X() -} - -federated reactor { - r1 = new R() - r2 = new R() -} \ No newline at end of file From aec74383513739ee7172ae873481e090dbec985c Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 13 Dec 2024 17:50:27 -0800 Subject: [PATCH 13/65] Make federated launch script executable --- .../org/lflang/generator/uc/UcFederatedPlatformGenerator.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt index 6386ad39..e372e4b2 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt @@ -55,6 +55,7 @@ class UcFederatedPlatformGenerator(generator: UcGenerator, val srcGenPath: Path) val launchScriptGenerator = UcFederatedLaunchScriptGenerator(fileConfig) FileUtil.writeToFile(launchScriptGenerator.generateLaunchScript(generator.getAllFederates()), fileConfig.binPath.resolve(fileConfig.name)) + fileConfig.binPath.resolve(fileConfig.name).toFile().setExecutable(true) val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); try { From d0b4e22b51b384b74d6afb93d4cce1cc5dc8e655 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 20 Dec 2024 10:00:22 +0100 Subject: [PATCH 14/65] WIP --- examples/posix/federated/receiver.c | 2 +- examples/posix/federated/sender.c | 2 +- .../zephyr/basic_federated/common/receiver.h | 2 +- .../federated_sender/src/sender.c | 4 +- include/reactor-uc/macros.h | 45 +-- .../platform/posix/tcp_ip_channel.h | 1 + include/reactor-uc/reactor-uc.h | 1 + include/reactor-uc/util.h | 5 + .../lflang/generator/uc/UcCmakeGenerator.kt | 1 + .../generator/uc/UcConnectionGenerator.kt | 305 +++++++++++++++--- .../generator/uc/UcFederateGenerator.kt | 15 +- .../org/lflang/generator/uc/UcGenerator.kt | 1 + .../lflang/generator/uc/UcMainGenerator.kt | 9 +- .../lflang/generator/uc/UcReactorGenerator.kt | 4 +- src/platform/posix/tcp_ip_channel.c | 7 +- src/schedulers/dynamic/scheduler.c | 4 +- src/util.c | 10 + test/lf/src/FederatedConnection.lf | 27 ++ 18 files changed, 364 insertions(+), 81 deletions(-) create mode 100644 test/lf/src/FederatedConnection.lf diff --git a/examples/posix/federated/receiver.c b/examples/posix/federated/receiver.c index f9ab15b7..95f18d44 100644 --- a/examples/posix/federated/receiver.c +++ b/examples/posix/federated/receiver.c @@ -57,7 +57,7 @@ typedef struct { TcpIpChannel channel; LF_FEDERATED_INPUT_CONNECTION_INSTANCE(Receiver, in); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(1, 0) -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Receiver, Sender); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Receiver, Sender); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Receiver, Sender) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); diff --git a/examples/posix/federated/sender.c b/examples/posix/federated/sender.c index fa8a39ab..b27e89b7 100644 --- a/examples/posix/federated/sender.c +++ b/examples/posix/federated/sender.c @@ -66,7 +66,7 @@ typedef struct { TcpIpChannel channel; LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(Sender, out); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(0, 1); -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Sender, Receiver); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Sender, Receiver); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); diff --git a/examples/zephyr/basic_federated/common/receiver.h b/examples/zephyr/basic_federated/common/receiver.h index db922e64..ff7c2d63 100644 --- a/examples/zephyr/basic_federated/common/receiver.h +++ b/examples/zephyr/basic_federated/common/receiver.h @@ -71,7 +71,7 @@ typedef struct { TcpIpChannel channel; LF_FEDERATED_INPUT_CONNECTION_INSTANCE(Receiver, in); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(1, 0) -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Receiver, Sender); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Receiver, Sender); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Receiver, Sender) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); diff --git a/examples/zephyr/basic_federated/federated_sender/src/sender.c b/examples/zephyr/basic_federated/federated_sender/src/sender.c index 8e4f0a0a..ee6e9566 100644 --- a/examples/zephyr/basic_federated/federated_sender/src/sender.c +++ b/examples/zephyr/basic_federated/federated_sender/src/sender.c @@ -104,14 +104,14 @@ typedef struct { TcpIpChannel channel; LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(Sender, out); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(0, 1); -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Sender, Receiver1); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Sender, Receiver1); typedef struct { FederatedConnectionBundle super; TcpIpChannel channel; LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(Sender, out); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(0, 1); -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Sender, Receiver2); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Sender, Receiver2); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver1) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index 8f5801c0..b89a28de 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -551,26 +551,30 @@ } typedef struct FederatedOutputConnection FederatedOutputConnection; -#define LF_DEFINE_FEDERATED_OUTPUT_CONNECTION(ReactorName, OutputName, BufferType, BufferSize) \ +// FIXME: What is needed here? +#define LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(ReactorName, OutputName, BufferType) \ typedef struct { \ FederatedOutputConnection super; \ - BufferType payload_buf[(BufferSize)]; \ - bool payload_used_buf[(BufferSize)]; \ - } ReactorName##_##OutputName##_conn; \ - \ + BufferType payload_buf[1]; \ + bool payload_used_buf[1]; \ + } ReactorName##_##OutputName##_conn; + +#define LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(ReactorName, OutputName, BufferType) \ void ReactorName##_##OutputName##_conn_ctor(ReactorName##_##OutputName##_conn *self, Reactor *parent, \ FederatedConnectionBundle *bundle) { \ FederatedOutputConnection_ctor(&self->super, parent, bundle, 0, (void *)&self->payload_buf, \ - (bool *)&self->payload_used_buf, sizeof(BufferType), BufferSize); \ + (bool *)&self->payload_used_buf, sizeof(BufferType), 1); \ } -#define LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(ReactorName, OutputName) \ - ReactorName##_##OutputName##_conn conn_##OutputName +#define LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(ReactorName, OutputName) ReactorName##_##OutputName##_conn OutputName -#define LF_FEDERATED_CONNECTION_BUNDLE_INSTANCE(ReactorName, OtherName) \ - ReactorName##_##OtherName##_Bundle ReactorName##_##OtherName##_bundle +#define LF_FEDERATED_CONNECTION_BUNDLE_TYPE(ReactorName, OtherName) ReactorName##_##OtherName##_Bundle -#define LF_FEDERATED_CONNECTION_BUNDLE_NAME(ReactorName, OtherName) ReactorName##_##OtherName##_Bundle +#define LF_FEDERATED_CONNECTION_BUNDLE_NAME(ReactorName, OtherName) ReactorName##_##OtherName##_bundle + +#define LF_FEDERATED_CONNECTION_BUNDLE_INSTANCE(ReactorName, OtherName) \ + LF_FEDERATED_CONNECTION_BUNDLE_TYPE(ReactorName, OtherName) \ + LF_FEDERATED_CONNECTION_BUNDLE_NAME(ReactorName, OtherName) #define LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(ReactorName, OtherName) \ void ReactorName##_##OtherName##_Bundle_ctor(ReactorName##_##OtherName##_Bundle *self, Reactor *parent) @@ -592,31 +596,32 @@ typedef struct FederatedOutputConnection FederatedOutputConnection; self->_bundles[_bundle_idx++] = &self->ReactorName##_##OtherName##_bundle.super; #define LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(ReactorName, OutputName, SerializeFunc) \ - ReactorName##_##OutputName##_conn_ctor(&self->conn_##OutputName, self->super.parent, &self->super); \ - self->outputs[_inputs_idx] = &self->conn_##OutputName.super; \ + ReactorName##_##OutputName##_conn_ctor(&self->OutputName, self->super.parent, &self->super); \ + self->outputs[_inputs_idx] = &self->OutputName.super; \ self->serialize_hooks[_inputs_idx] = SerializeFunc; \ _outputs_idx++; typedef struct FederatedInputConnection FederatedInputConnection; -#define LF_DEFINE_FEDERATED_INPUT_CONNECTION(ReactorName, InputName, BufferType, BufferSize, Delay, IsPhysical) \ +#define LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(ReactorName, InputName, BufferType, BufferSize) \ typedef struct { \ FederatedInputConnection super; \ BufferType payload_buf[(BufferSize)]; \ bool payload_used_buf[(BufferSize)]; \ Port *downstreams[1]; \ - } ReactorName##_##InputName##_conn; \ - \ - void ReactorName##_##InputName##_conn_ctor(ReactorName##_##InputName##_conn *self, Reactor *parent) { \ + } ReactorName##_##InputName; + +#define LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(ReactorName, InputName, BufferType, BufferSize, Delay, IsPhysical) \ + void ReactorName##_##InputName##_conn_ctor(ReactorName##_##InputName *self, Reactor *parent) { \ FederatedInputConnection_ctor(&self->super, parent, Delay, IsPhysical, (Port **)&self->downstreams, 1, \ (void *)&self->payload_buf, (bool *)&self->payload_used_buf, sizeof(BufferType), \ BufferSize); \ } -#define LF_FEDERATED_INPUT_CONNECTION_INSTANCE(ReactorName, InputName) ReactorName##_##InputName##_conn conn_##InputName +#define LF_FEDERATED_INPUT_CONNECTION_INSTANCE(ReactorName, InputName) ReactorName##_##InputName InputName #define LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(ReactorName, InputName, DeserializeFunc) \ - ReactorName##_##InputName##_conn_ctor(&self->conn_##InputName, self->super.parent); \ - self->inputs[_inputs_idx] = &self->conn_##InputName.super; \ + ReactorName##_##InputName##_conn_ctor(&self->InputName, self->super.parent); \ + self->inputs[_inputs_idx] = &self->InputName.super; \ self->deserialize_hooks[_inputs_idx] = DeserializeFunc; \ _inputs_idx++; diff --git a/include/reactor-uc/platform/posix/tcp_ip_channel.h b/include/reactor-uc/platform/posix/tcp_ip_channel.h index 52feb6e3..4a684cf0 100644 --- a/include/reactor-uc/platform/posix/tcp_ip_channel.h +++ b/include/reactor-uc/platform/posix/tcp_ip_channel.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "proto/message.pb.h" #include "reactor-uc/error.h" diff --git a/include/reactor-uc/reactor-uc.h b/include/reactor-uc/reactor-uc.h index f7be4828..041e8cd1 100644 --- a/include/reactor-uc/reactor-uc.h +++ b/include/reactor-uc/reactor-uc.h @@ -13,6 +13,7 @@ #include "reactor-uc/builtin_triggers.h" #include "reactor-uc/connection.h" #include "reactor-uc/environment.h" +#include "reactor-uc/network_channel.h" #include "reactor-uc/error.h" #include "reactor-uc/federated.h" #include "reactor-uc/logging.h" diff --git a/include/reactor-uc/util.h b/include/reactor-uc/util.h index 5fb0c7e0..0b4ee8ec 100644 --- a/include/reactor-uc/util.h +++ b/include/reactor-uc/util.h @@ -6,4 +6,9 @@ void lf_connect(Connection *connection, Port *upstream, Port *downstream); +void lf_connect_federated_output(Connection *connection, Port *output); + +void lf_connect_federated_input(Connection *connection, Port *input); + + #endif \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index fcb40ec8..ac1c7769 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -59,6 +59,7 @@ class UcCmakeGenerator(private val mainDef: Instantiation, private val targetCon |set(REACTION_QUEUE_SIZE ${max(main.getReactionQueueSize(), 1)} CACHE STRING "Size of the reaction queue") |set(EVENT_QUEUE_SIZE ${max(main.getEventQueueSize(), 1)} CACHE STRING "Size of the event queue") |set(CMAKE_BUILD_TYPE ${targetConfig.getOrDefault(BuildTypeProperty.INSTANCE)}) + |${if (mainDef.isAFederate) "set(NETWORK_CHANNEL_TCP_POSIX ON CACHE BOOL \"Use TcpIpChannel\")" else ""} | |set(LF_MAIN_TARGET ${mainTarget}) |set(SOURCES diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index d0aa04b0..172025d9 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -3,6 +3,9 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.generator.orNever +import org.lflang.generator.uc.UcConnectionGenerator.Companion.isFederated +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate +import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.width import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType @@ -11,19 +14,25 @@ import org.lflang.lf.* // Representing a runtime connection with a single upstream port. An optimization is to group // all identical connections (with same delay and is_physical) in a single connection -class UcGroupedConnection(val varRef: VarRef, val conn: Connection, val uid: Int) { +class UcGroupedConnection(val srcVarRef: VarRef, val dstVarRef: VarRef, val conn: Connection, val uid: Int) { private val dests = mutableListOf>>() private var destsBuffer = mutableListOf>() - val srcInst: Instantiation? = varRef.container - val srcPort = varRef.variable as Port - val bankWidth = srcInst?.width?:1 + val srcInst: Instantiation? = srcVarRef.container + val dstInst: Instantiation? = + dstVarRef.container // FIXME: A little hacky since it only makes sense for FederatedGroupConnections + val srcPort = srcVarRef.variable as Port + val bankWidth = srcInst?.width ?: 1 val portWidth = srcPort.width - val uniqueName = "conn_${srcInst?.name?:""}_${srcPort.name}_${uid}" + val uniqueName = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" val maxNumPendingEvents = 16 // FIXME: Must be derived from the program val delay = conn.delay.orNever().toCCode() val isPhysical = conn.isPhysical val isLogical = !isPhysical && conn.delay == null + val isFederated = srcInst != null && srcInst.isAFederate + val serializeFunc = "serialize_payload_default" + val deserializeFunc = "deserialize_payload_default" + fun numDownstreams() = dests.size @@ -39,6 +48,22 @@ class UcGroupedConnection(val varRef: VarRef, val conn: Connection, val uid: Int } } +class UcFederatedConnectionBundle( + val src: Instantiation, + val dst: Instantiation, + val groupedConnections: List +) { + val channelCodeType = "TcpIpChannel" + + fun numInputs(federate: Instantiation) = groupedConnections.count { it.dstInst == federate } + + fun numOutputs(federate: Instantiation) = groupedConnections.count { it.srcInst == federate } + + fun generateChannelCtor(federate: Instantiation) = + "TcpIpChannel_ctor(&self->channel, parent->env, \"127.0.0.1\", 10000 + ${groupedConnections.first().uid}, AF_INET, ${(federate == src).toString()});" + +} + // Convenience class around a port variable reference. It is used to encapsulate the management of multi-connections // where a single lhs port has to class UcChannel(val varRef: VarRef, val port_idx: Int, val bank_idx: Int) { @@ -54,7 +79,7 @@ class UcChannel(val varRef: VarRef, val port_idx: Int, val bank_idx: Int) { // For each connection statement where a port is referenced, we create an UcPort and use this class // to figure out how the individual channels are connect to other UcPorts. class UcPort(val varRef: VarRef) { - val bankWidth = varRef.container?.width?:1 + val bankWidth = varRef.container?.width ?: 1 val portWidth = (varRef.variable as Port).width private val isInterleaved = varRef.isInterleaved private val channels = ArrayDeque() @@ -63,14 +88,14 @@ class UcPort(val varRef: VarRef) { // then we create channels first for ports then for banks. init { if (isInterleaved) { - for (i in 0..portWidth-1) { - for (j in 0..bankWidth-1) { + for (i in 0..portWidth - 1) { + for (j in 0..bankWidth - 1) { channels.add(UcChannel(varRef, i, j)) } } } else { - for (i in 0..bankWidth-1) { - for (j in 0..portWidth-1) { + for (i in 0..bankWidth - 1) { + for (j in 0..portWidth - 1) { channels.add(UcChannel(varRef, j, i)) } } @@ -103,9 +128,9 @@ class UcConnections() { // to one variable and others to another. fun addConnection(conn: Connection) { // First translate the variables into our UcPort which also has information of channels (banks x multiports) - val rhsPorts= conn.rightPorts.map {UcPort(it)} + val rhsPorts = conn.rightPorts.map { UcPort(it) } var rhsPortIndex = 0 - var lhsPorts= conn.leftPorts.map{UcPort(it)} + var lhsPorts = conn.leftPorts.map { UcPort(it) } var lhsPortIndex = 0 // Keep parsing out connections until we are out of right-hand-side (rhs) ports @@ -113,14 +138,14 @@ class UcConnections() { // First get the current lhs and rhs port and UcGroupedConnection that we are working with val lhsPort = lhsPorts.get(lhsPortIndex) val rhsPort = rhsPorts.get(rhsPortIndex) - val ucConnection = getOrCreateNewGroupedConnection(lhsPort.varRef, conn) + val ucConnection = getOrCreateNewGroupedConnection(lhsPort.varRef, rhsPort.varRef, conn) if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { // If we have more channels left in the rhs variable, then we "complete" a downstreamSet fo // the lhs, commit it, and move to the next lhs variable val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) val lhsChannelsToAdd = lhsPort.takeRemainingChannels() - lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach{ ucConnection.addDest(it)} + lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach { ucConnection.addDest(it) } ucConnection.commit() lhsPortIndex += 1 } else if (rhsPort.channelsLeft() < lhsPort.channelsLeft()) { @@ -129,14 +154,15 @@ class UcConnections() { val numRhsChannelsToAdd = rhsPort.channelsLeft() val rhsChannelsToAdd = rhsPort.takeRemainingChannels() val lhsChannelsToAdd = lhsPort.takeChannels(numRhsChannelsToAdd) - lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach{ ucConnection.addDest(it)} + lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach { ucConnection.addDest(it) } rhsPortIndex += 1 if (rhsPortIndex >= rhsPorts.size) { ucConnection.commit() } } else { // Channels are same size - lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()).forEach { ucConnection.addDest(it) } + lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()) + .forEach { ucConnection.addDest(it) } ucConnection.commit() rhsPortIndex += 1 lhsPortIndex += 1 @@ -147,22 +173,31 @@ class UcConnections() { // we have been thorugh all rhs channels. if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { assert(conn.isIterated) - lhsPorts = conn.leftPorts.map{UcPort(it)} + lhsPorts = conn.leftPorts.map { UcPort(it) } lhsPortIndex = 0 } - } } + } /** Finds an existing GroupedConnection from srcVarRef with matchin connection properties (physical and delay). */ - fun findExistingGroupedConnection(srcVarRef: VarRef, conn: Connection): UcGroupedConnection? { - return connections.find { c -> c.varRef == srcVarRef && c.isPhysical == conn.isPhysical && c.delay == conn.delay.orNever().toCCode()} + fun findExistingGroupedConnection(srcVarRef: VarRef, dstVarRef: VarRef, conn: Connection): UcGroupedConnection? { + if (conn.isFederated) { + return connections.find { c -> + c.srcVarRef == srcVarRef && c.isPhysical == conn.isPhysical && c.delay == conn.delay.orNever().toCCode() + } + } else { + return connections.find { c -> + c.srcVarRef == srcVarRef && c.dstInst == dstVarRef.container && c.isPhysical == conn.isPhysical && c.delay == conn.delay.orNever() + .toCCode() + } + } } /** Finds an existing grouped connection, or creates a new.*/ - fun getOrCreateNewGroupedConnection(srcVarRef: VarRef, conn: Connection): UcGroupedConnection { - var res = findExistingGroupedConnection(srcVarRef, conn) + fun getOrCreateNewGroupedConnection(srcVarRef: VarRef, dstVarRef: VarRef, conn: Connection): UcGroupedConnection { + var res = findExistingGroupedConnection(srcVarRef, dstVarRef, conn) if (res == null) { - res = UcGroupedConnection(srcVarRef, conn, connections.size) + res = UcGroupedConnection(srcVarRef, dstVarRef, conn, connections.size) connections.add(res) } return res @@ -171,54 +206,240 @@ class UcConnections() { /** Find the number of grouped connections coming out of a particular port. This is needed to know how many * Connection pointers to allocated on the self-struct of a reactor containing another reactor with an output port. */ fun findNumGroupedConnectionsFromPort(srcInst: Instantiation?, srcPort: Port) = - connections.filter{ c -> c.srcPort == srcPort && c.srcInst == srcInst}.size + connections.filter { c -> c.srcPort == srcPort && c.srcInst == srcInst }.size + + fun getFederatedConnectionBundles(): List { + val res = mutableListOf() + val connectionsGroupedByBundle: Map, List> = + connections.groupBy { Pair(it.srcInst!!, it.dstInst!!) } + + for ((key, value) in connectionsGroupedByBundle) { + val bundle = UcFederatedConnectionBundle(key.first, key.second, value) + res.add(bundle) + } + return res + } } -class UcConnectionGenerator(private val reactor: Reactor) { +class UcConnectionGenerator(private val reactor: Reactor, private val federate: Instantiation?) { private val ucConnections = UcConnections() + private val ucFederatedConnectionBundles: List + init { reactor.allConnections.forEach { ucConnections.addConnection(it) } + if (federate != null) { + ucConnections.connections.removeIf { it.srcInst != federate && it.dstInst != federate } + ucFederatedConnectionBundles = ucConnections.getFederatedConnectionBundles() + } else { + ucFederatedConnectionBundles = emptyList() + } + } + + companion object { + val Connection.isFederated + get(): Boolean = (this.eContainer() as Reactor).isFederated } + fun getNumFederatedConnectionBundles() = ucFederatedConnectionBundles.size + fun getNumConnectionsFromPort(instantiation: Instantiation?, port: Port): Int { return ucConnections.findNumGroupedConnectionsFromPort(instantiation, port) } - private fun generateLogicalSelfStruct(conn: UcGroupedConnection) = "LF_DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()})"; - private fun generateLogicalCtor(conn: UcGroupedConnection) = "LF_DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()})"; + private fun generateLogicalSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()});" + + private fun generateLogicalCtor(conn: UcGroupedConnection) = + "LF_DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()});" + + private fun generateDelayedSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay});" + + private fun generateDelayedCtor(conn: UcGroupedConnection) = + "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" + + private fun generateFederatedInputSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" + + private fun generateFederatedInputCtor(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" + + private fun generateFederatedOutputSelfStruct(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()});" + + private fun generateFederatedOutputCtor(conn: UcGroupedConnection) = + "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()});" + + private fun generateFederatedConnectionSelfStruct(conn: UcGroupedConnection) = + if (conn.srcInst == federate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct(conn) + + private fun generateFederatedConnectionCtor(conn: UcGroupedConnection) = + if (conn.srcInst == federate) generateFederatedOutputCtor(conn) else generateFederatedInputCtor(conn) + + private fun generateFederatedOutputInstance(conn: UcGroupedConnection) = + "LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.uniqueName});" + + private fun generateFederatedInputInstance(conn: UcGroupedConnection) = + "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.uniqueName});" - private fun generateDelayedSelfStruct(conn: UcGroupedConnection) = "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay})"; - private fun generateDelayedCtor(conn: UcGroupedConnection) = "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical})"; + private fun generateFederatedConnectionInstance(conn: UcGroupedConnection) = + if (conn.srcInst == federate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance(conn) + private fun generateInitializeFederatedOutput(conn: UcGroupedConnection) = + "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.uniqueName}, ${conn.serializeFunc});" - private fun generateReactorCtorCode(conn: UcGroupedConnection) = with(PrependOperator) { + private fun generateInitializeFederatedInput(conn: UcGroupedConnection) = + "LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(${reactor.codeType}, ${conn.uniqueName}, ${conn.deserializeFunc});" + + private fun generateInitializeFederatedConnection(conn: UcGroupedConnection) = + if (conn.srcInst == federate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput(conn) + + + private fun generateReactorCtorCode(conn: UcGroupedConnection) = with(PrependOperator) { """ |${if (conn.isLogical) "LF_INITIALIZE_LOGICAL_CONNECTION(" else "LF_INITIALIZE_DELAYED_CONNECTION("}${reactor.codeType}, ${conn.uniqueName}, ${conn.bankWidth}, ${conn.portWidth}); """.trimMargin() }; - private fun generateConnectChannel(conn: UcGroupedConnection, channels: Pair ) = with(PrependOperator) { - """|lf_connect((Connection *) &self->${conn.uniqueName}[${channels.first.bank_idx}][${channels.first.port_idx}], (Port *) ${channels.first.generateChannelPointer()}, (Port *) ${channels.second.generateChannelPointer()}); + private fun generateFederateCtorCode(conn: UcFederatedConnectionBundle) = + "LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(${conn.src.codeTypeFederate}, ${conn.dst.codeTypeFederate});" + + private fun generateConnectChannel(conn: UcGroupedConnection, channels: Pair) = + with(PrependOperator) { + """|lf_connect((Connection *) &self->${conn.uniqueName}[${channels.first.bank_idx}][${channels.first.port_idx}], (Port *) ${channels.first.generateChannelPointer()}, (Port *) ${channels.second.generateChannelPointer()}); """.trimMargin() - } + } private fun generateConnectionStatements(conn: UcGroupedConnection) = with(PrependOperator) { - conn.getDests().joinToString(separator="\n"){it.joinToString(separator = "\n") { generateConnectChannel(conn, it)}} + conn.getDests() + .joinToString(separator = "\n") { it.joinToString(separator = "\n") { generateConnectChannel(conn, it) } } } - fun generateReactorCtorCodes() = - ucConnections.connections.joinToString(prefix = "// Initialize connections\n", separator = "\n", postfix = "\n") { generateReactorCtorCode(it)} + - ucConnections.connections.joinToString(prefix = "// Do connections \n", separator = "\n", postfix = "\n") { generateConnectionStatements(it)} + private fun generateConnectFederateOutputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection) = + conn.getDests().joinWithLn { + it.joinWithLn { + "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}).${conn.uniqueName}, (Port*) &self->${bundle.src.name}[0].${it.first.varRef.name}[0]);" + } + } + + private fun generateConnectFederateInputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection) = + conn.getDests().joinWithLn { + it.joinWithLn { + "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}).${conn.uniqueName}, (Port*) &self->${bundle.dst.name}[0].${it.second.varRef.name}[0]);" + } + } + + + private fun generateFederateConnectionStatements(conn: UcFederatedConnectionBundle) = + conn.groupedConnections.joinWithLn { + if (conn.src == federate) { + generateConnectFederateOutputChannel(conn, it) + } else { + generateConnectFederateInputChannel(conn, it) + } + } - fun generateCtors() = ucConnections.connections.joinToString(prefix = "// Connection constructors\n", separator = "\n", postfix = "\n"){ - if(it.conn.isPhysical || it.conn.delay != null) generateDelayedCtor(it) + fun generateFederateCtorCodes() = + ucFederatedConnectionBundles.joinToString( + prefix = "// Initialize connection bundles\n", + separator = "\n", + postfix = "\n" + ) { generateFederateCtorCode(it) } + + ucFederatedConnectionBundles.joinToString( + prefix = "// Do connections \n", + separator = "\n", + postfix = "\n" + ) { generateFederateConnectionStatements(it) } + + fun generateReactorCtorCodes() = + ucConnections.connections.joinToString( + prefix = "// Initialize connections\n", + separator = "\n", + postfix = "\n" + ) { generateReactorCtorCode(it) } + + ucConnections.connections.joinToString( + prefix = "// Do connections \n", + separator = "\n", + postfix = "\n" + ) { generateConnectionStatements(it) } + + fun generateCtors() = ucConnections.connections.joinToString( + prefix = "// Connection constructors\n", + separator = "\n", + postfix = "\n" + ) { + if (it.isFederated) generateFederatedConnectionCtor(it) + else if (it.conn.isPhysical || it.conn.delay != null) generateDelayedCtor(it) else generateLogicalCtor(it) } - fun generateSelfStructs() = ucConnections.connections.joinToString(prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { - if (it.isLogical) generateLogicalSelfStruct(it) - else generateDelayedSelfStruct(it) + fun generateSelfStructs() = + ucConnections.connections.joinToString(prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { + if (it.isFederated) generateFederatedConnectionSelfStruct(it) + else if (it.isLogical) generateLogicalSelfStruct(it) + else generateDelayedSelfStruct(it) + } + + + fun generateFederatedConnectionBundleSelfStruct(bundle: UcFederatedConnectionBundle) = with(PrependOperator) { + """ |typedef struct { + | FederatedConnectionBundle super; + ${" | "..bundle.channelCodeType} channel; + ${" | "..bundle.groupedConnections.joinWithLn { generateFederatedConnectionInstance(it) }} + | LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(${bundle.numInputs(federate!!)}, ${ + bundle.numOutputs( + federate!! + ) + }); + |} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}); + """.trimMargin() + } + + fun generateFederatedConnectionBundleCtor(bundle: UcFederatedConnectionBundle) = with(PrependOperator) { + """ |LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}) { + | LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); + | ${bundle.generateChannelCtor(federate!!)} + | LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); + ${" | "..bundle.groupedConnections.joinWithLn { generateInitializeFederatedConnection(it) }} + |} + """.trimMargin() } + + fun generateFederatedSelfStructs() = ucFederatedConnectionBundles.joinToString( + prefix = "// Federated Connections\n", + separator = "\n", + postfix = "\n" + ) { + it.groupedConnections.joinToString( + prefix = "// Federated input and output connection self structs\n", + separator = "\n", + postfix = "\n" + ) { generateFederatedConnectionSelfStruct(it) } + + generateFederatedConnectionBundleSelfStruct(it) + } + + fun generateFederatedCtors() = ucFederatedConnectionBundles.joinToString( + prefix = "// Federated Connections\n", + separator = "\n", + postfix = "\n" + ) { + it.groupedConnections.joinToString( + prefix = "// Federated input and output connection constructors\n", + separator = "\n", + postfix = "\n" + ) { generateFederatedConnectionCtor(it) } + + generateFederatedConnectionBundleCtor(it) + } + + + fun generateFederateStructFields() = ucFederatedConnectionBundles.joinToString( + prefix = "// Federated Connections\n", + separator = "\n", + postfix = "\n" + ) { + "LF_FEDERATED_CONNECTION_BUNDLE_INSTANCE(${it.src.codeTypeFederate}, ${it.dst.codeTypeFederate});" + } + fun generateReactorStructFields() = ucConnections.connections.joinToString(prefix = "// Connections \n", separator = "\n", postfix = "\n") { if (it.isLogical) "LF_LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.uniqueName}, ${it.bankWidth}, ${it.portWidth});" diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index f1a5d617..9209c30d 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -11,7 +11,7 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC private val container = federate.eContainer() as Reactor private val reactor = federate.reactor - private val connections = UcConnectionGenerator(container) + private val connections = UcConnectionGenerator(container, federate) private val parameters = UcParameterGenerator(container) private val ports = UcPortGenerator(container, connections) private val reactions = UcReactionGenerator(container) @@ -19,18 +19,16 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC private val headerFile = "Federate.h" fun numBundles() = 1 + // FIXME: Calculate event queue size for federate... private val includeGuard = "LFC_GEN_FEDERATE_${federate.name.uppercase()}_H" - private fun generateFederateStruct() = with(PrependOperator) { """ |typedef struct { | Reactor super; ${" | "..instances.generateReactorStructField(federate)} - ${" | "..connections.generateReactorStructFields()} - ${" | "..instances.generateReactorStructContainedInputFields(federate)} - ${" | "..instances.generateReactorStructContainedOutputFields(federate)} + ${" | "..connections.generateFederateStructFields()} | LF_FEDERATE_BOOKKEEPING_INSTANCES(${numBundles()}); |} ${federate.codeTypeFederate}; | @@ -43,7 +41,7 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC | LF_FEDERATE_CTOR_PREAMBLE(); | LF_REACTOR_CTOR(${federate.codeTypeFederate}); ${" | "..instances.generateReactorCtorCode(federate)} - ${" | "..connections.generateReactorCtorCodes()} + ${" | "..connections.generateFederateCtorCodes()} |} | """.trimMargin() @@ -58,7 +56,8 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC |#include "reactor-uc/reactor-uc.h" |#include "${fileConfig.getReactorHeaderPath(reactor).toUnixString()}" | - ${" |"..connections.generateSelfStructs()} + |#include "reactor-uc/platform/posix/tcp_ip_channel.h" + ${" |"..connections.generateFederatedSelfStructs()} ${" |"..generateFederateStruct()} ${" |"..generateCtorDeclaration()}; |#endif // ${includeGuard} @@ -69,7 +68,7 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC """ |#include "${headerFile}" | - ${" |"..connections.generateCtors()} + ${" |"..connections.generateFederatedCtors()} ${" |"..generateCtorDefinition()} | """.trimMargin() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index 45556edd..eb161f33 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -94,6 +94,7 @@ class UcGenerator( for (federate in federates) { mainDef = federate codeMaps.clear() + ucSources.clear() val srcGenPath = fileConfig.srcGenPath.resolve(federate.name) val res = doGenerateTopLevel(resource, context, srcGenPath, UcFederatedPlatformGenerator(this, srcGenPath)) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index 43b8a090..f88b7161 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -8,6 +8,7 @@ import org.lflang.inferredType import org.lflang.lf.Instantiation import org.lflang.lf.Parameter import org.lflang.lf.Reactor +import org.lflang.reactor import org.lflang.target.property.FastProperty import org.lflang.target.property.KeepaliveProperty import org.lflang.target.property.PlatformProperty @@ -22,7 +23,9 @@ class UcMainGenerator( private val fileConfig: UcFileConfig, ) { + private val top = mainDef.eContainer() as Reactor private val ucParameterGenerator = UcParameterGenerator(main) + private val ucConnectionGenerator = UcConnectionGenerator(top, mainDef) fun getDuration() = if (targetConfig.isSet(TimeOutProperty.INSTANCE)) targetConfig.get(TimeOutProperty.INSTANCE).toCCode() else "FOREVER" @@ -63,10 +66,14 @@ class UcMainGenerator( |} |void lf_start(void) { | Environment_ctor(&lf_environment, (Reactor *)&main_reactor); - | ${mainDef.codeTypeFederate}_ctor(&main_reactor, NULL, &lf_environment); | lf_environment.scheduler->duration = ${getDuration()}; | lf_environment.scheduler->keep_alive = ${keepAlive()}; + | lf_environment.scheduler->leader = ${top.instantiations.first() == mainDef}; | lf_environment.fast_mode = ${fast()}; + | lf_environment.has_async_events = ${mainDef.reactor.inputs.isNotEmpty()}; + | ${mainDef.codeTypeFederate}_ctor(&main_reactor, NULL, &lf_environment); + | lf_environment.net_bundles_size = ${ucConnectionGenerator.getNumFederatedConnectionBundles()}; + | lf_environment.net_bundles = (FederatedConnectionBundle **) &main_reactor._bundles; | lf_environment.assemble(&lf_environment); | lf_environment.start(&lf_environment); | lf_exit(); diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt index 58186f52..b9165bba 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -29,7 +29,7 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U private val numChildren = reactor.allInstantiations.map { it.width }.sum() private val parameters = UcParameterGenerator(reactor) - private val connections = UcConnectionGenerator(reactor) + private val connections = UcConnectionGenerator(reactor, null) private val state = UcStateGenerator(reactor) private val ports = UcPortGenerator(reactor, connections) private val timers = UcTimerGenerator(reactor) @@ -93,7 +93,7 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U if (hasShutdown) currentReactorsEvents += 1 if (hasStartup) currentReactorsEvents += 1 - val ucConnections = UcConnectionGenerator(this) + val ucConnections = UcConnectionGenerator(this, null) currentReactorsEvents += ucConnections.getMaxNumPendingEvents() return childrenEvents + currentReactorsEvents } diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index a64cd3d9..41510b67 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -307,6 +307,7 @@ static lf_ret_t _TcpIpChannel_receive(NetworkChannel *untyped_self, FederateMess case ENOTCONN: case ECONNABORTED: TCP_IP_CHANNEL_ERR("Error recv from socket errno=%d", errno); + _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_LOST_CONNECTION); return LF_ERR; case EAGAIN: /* The socket has no new data to receive */ @@ -315,6 +316,7 @@ static lf_ret_t _TcpIpChannel_receive(NetworkChannel *untyped_self, FederateMess continue; } else if (bytes_read == 0) { // This means the connection was closed. + _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CLOSED); TCP_IP_CHANNEL_DEBUG("Other federate gracefully closed socket"); return LF_ERR; } @@ -421,7 +423,7 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { validate(self->receive_callback); self->receive_callback(self->federated_connection, &self->output); } else if (ret == LF_ERR) { - _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_LOST_CONNECTION); + /* Return to see what the error was by inspecting the network channel state.*/ } } else if (FD_ISSET(self->send_failed_event_fds, &readfds)) { TCP_IP_CHANNEL_DEBUG("Select -> cancelled by send_block failure"); @@ -431,7 +433,10 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { } break; case NETWORK_CHANNEL_STATE_UNINITIALIZED: + break; case NETWORK_CHANNEL_STATE_CLOSED: + TcpIpChannel_close_connection(untyped_self); + self->terminate = true; break; } } diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index f41b1c44..6dd19453 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -67,7 +67,7 @@ static lf_ret_t Scheduler_federated_acquire_tag(Scheduler *untyped_self, tag_t n if (lf_tag_compare(input->last_known_tag, next_tag) < 0) { LF_DEBUG(SCHED, "Input %p is unresolved, latest known tag was %" PRId64 ":%" PRIu32, input, input->last_known_tag.time, input->last_known_tag.microstep); - LF_DEBUG(SCHED, "Input %p has STAA of %" PRId64, input->safe_to_assume_absent); + LF_DEBUG(SCHED, "Input %p has STAA of %" PRId64, input, input->safe_to_assume_absent); if (input->safe_to_assume_absent > additional_sleep) { additional_sleep = input->safe_to_assume_absent; } @@ -77,7 +77,7 @@ static lf_ret_t Scheduler_federated_acquire_tag(Scheduler *untyped_self, tag_t n if (additional_sleep > 0) { LF_DEBUG(SCHED, "Need to sleep for additional %" PRId64 " ns", additional_sleep); - instant_t sleep_until = lf_time_add(env->get_logical_time(env), additional_sleep); + instant_t sleep_until = lf_time_add(next_tag.time, additional_sleep); return env->wait_until(env, sleep_until); } else { return LF_OK; diff --git a/src/util.c b/src/util.c index 9c6c0f79..34064959 100644 --- a/src/util.c +++ b/src/util.c @@ -8,3 +8,13 @@ void lf_connect(Connection *connection, Port *upstream, Port *downstream) { connection->upstream = upstream; connection->register_downstream(connection, downstream); } + +void lf_connect_federated_output(Connection *connection, Port *output) { + validate(output->conns_out_registered < output->conns_out_size); + output->conns_out[output->conns_out_registered++] = connection; + connection->upstream = output; +} + +void lf_connect_federated_input(Connection *connection, Port *input) { + connection->register_downstream(connection, input); +} diff --git a/test/lf/src/FederatedConnection.lf b/test/lf/src/FederatedConnection.lf new file mode 100644 index 00000000..704d8924 --- /dev/null +++ b/test/lf/src/FederatedConnection.lf @@ -0,0 +1,27 @@ +target uC { + platform: Native +} + +reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} +} + +reactor Dst { + input in: int + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + =} +} + +federated reactor { + r1 = new Src(id=42) + r2 = new Dst() + r1.out -> r2.in +} \ No newline at end of file From 8efd0657fc95ca277bb4be81448c6450265abd3e Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 3 Jan 2025 11:14:14 +0100 Subject: [PATCH 15/65] Rework the handling of connections --- .../generator/uc/UcConnectionGenerator.kt | 421 +++++++++++------- 1 file changed, 269 insertions(+), 152 deletions(-) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 172025d9..521740d1 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -12,6 +12,123 @@ import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.lf.* +class UcFederate(val federate: Instantiation, bankIdx: Int) { + val isBank = federate.isBank +} + +class UcConnection(val src: VarRef) { + +} + +class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { + + companion object { + fun parseConnectionChannels(conn: Connection): List { + val res = mutableListOf() + val rhsPorts = conn.rightPorts.map { UcPort(it) } + var rhsPortIndex = 0 + var lhsPorts = conn.leftPorts.map { UcPort(it) } + var lhsPortIndex = 0 + + // Keep parsing out connections until we are out of right-hand-side (rhs) ports + while (rhsPortIndex < rhsPorts.size) { + // First get the current lhs and rhs port and UcGroupedConnection that we are working with + val lhsPort = lhsPorts.get(lhsPortIndex) + val rhsPort = rhsPorts.get(rhsPortIndex) + if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { + val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) + val lhsChannelsToAdd = lhsPort.takeRemainingChannels() + lhsChannelsToAdd.zip(rhsChannelsToAdd) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } + lhsPortIndex += 1 + } else if (rhsPort.channelsLeft() < lhsPort.channelsLeft()) { + val numRhsChannelsToAdd = rhsPort.channelsLeft() + val rhsChannelsToAdd = rhsPort.takeRemainingChannels() + val lhsChannelsToAdd = lhsPort.takeChannels(numRhsChannelsToAdd) + lhsChannelsToAdd.zip(rhsChannelsToAdd) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } + rhsPortIndex += 1 + } else { + lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } + rhsPortIndex += 1 + lhsPortIndex += 1 + } + + // If we are out of lhs variables, but not rhs, then there should be an iterated connection. + // We handle it by resetting the lhsChannels variable and index and continuing until + // we have been thorugh all rhs channels. + if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { + assert(conn.isIterated) + lhsPorts = conn.leftPorts.map { UcPort(it) } + lhsPortIndex = 0 + } + } + return res + } + } +} + +open class UcGroupedConnection2(val src: VarRef, val channels: List, val lfConn: Connection, val uid: Int) { + val delay = lfConn.delay + val isPhysical = lfConn.isPhysical + val isLogical = lfConn.isLogical + val srcInst = src.container + val srcPort = src.variable as Port + + val isFederated = lfConn.isFederated && channels.first().dest.varRef.container != srcInst + val serializeFunc = "serialize_payload_default" + val deserializeFunc = "deserialize_payload_default" + val bankWidth = srcInst?.width ?: 1 + val portWidth = srcPort.width + val uniqueName = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" + val numDownstreams = { + val frequencyMap = channels.groupingBy { Pair(it.src.port_idx, it.src.bank_idx)}.eachCount() + frequencyMap.values.maxOrNull() ?:0 + } + val maxNumPendingEvents = 16 // FIXME: Must be derived from the program + + + companion object { + private val Connection.delayString + get(): String = this.delay.orNever().toCCode() + private val Connection.isLogical + get(): Boolean = !this.isPhysical && this.delay == null + + fun groupConnections(channels: List): List { + val res = mutableListOf() + val channels = HashSet(channels) + + while (channels.isNotEmpty()) { + val c = channels.first()!! + val others = if (c.conn.isFederated) { + channels.filter { + it.conn.delayString == c.conn.delayString && + it.conn.isPhysical == c.conn.isPhysical && + it.dest.varRef == c.dest.varRef + it.src.varRef == c.src.varRef + } + } else { + channels.filter { + it.conn.delayString == c.conn.delayString && + it.conn.isPhysical == c.conn.isPhysical && + it.src.varRef == c.src.varRef + } + } + + val groupedChannels = others.plus(c) + val groupedConnection = UcGroupedConnection2(c.src.varRef, groupedChannels, c.conn, res.size) + res.add(groupedConnection) + } + return res + } + } +} + +class UcFederatedConnectionBundle2(val src: UcFederate, val dest: UcFederate, val netChannel: UcNetworkChannel) { +} + + // Representing a runtime connection with a single upstream port. An optimization is to group // all identical connections (with same delay and is_physical) in a single connection class UcGroupedConnection(val srcVarRef: VarRef, val dstVarRef: VarRef, val conn: Connection, val uid: Int) { @@ -51,11 +168,11 @@ class UcGroupedConnection(val srcVarRef: VarRef, val dstVarRef: VarRef, val conn class UcFederatedConnectionBundle( val src: Instantiation, val dst: Instantiation, - val groupedConnections: List + val groupedConnections: List ) { val channelCodeType = "TcpIpChannel" - fun numInputs(federate: Instantiation) = groupedConnections.count { it.dstInst == federate } + fun numInputs(federate: Instantiation) = groupedConnections.count { it.channels.first().dest.varRef.container == federate } fun numOutputs(federate: Instantiation) = groupedConnections.count { it.srcInst == federate } @@ -118,121 +235,119 @@ class UcPort(val varRef: VarRef) { fun channelsLeft(): Int = channels.size } - -// A class for maintaining all the runtime UcConnections within a reactor. -class UcConnections() { - val connections = mutableListOf(); - - // Parse a connection and update the list of UcGroupedConnection. This is non-trivial since a single variable - // in the connection can have several banks and multiports. In a multi-connection, we might connect some channels - // to one variable and others to another. - fun addConnection(conn: Connection) { - // First translate the variables into our UcPort which also has information of channels (banks x multiports) - val rhsPorts = conn.rightPorts.map { UcPort(it) } - var rhsPortIndex = 0 - var lhsPorts = conn.leftPorts.map { UcPort(it) } - var lhsPortIndex = 0 - - // Keep parsing out connections until we are out of right-hand-side (rhs) ports - while (rhsPortIndex < rhsPorts.size) { - // First get the current lhs and rhs port and UcGroupedConnection that we are working with - val lhsPort = lhsPorts.get(lhsPortIndex) - val rhsPort = rhsPorts.get(rhsPortIndex) - val ucConnection = getOrCreateNewGroupedConnection(lhsPort.varRef, rhsPort.varRef, conn) - - if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { - // If we have more channels left in the rhs variable, then we "complete" a downstreamSet fo - // the lhs, commit it, and move to the next lhs variable - val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) - val lhsChannelsToAdd = lhsPort.takeRemainingChannels() - lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach { ucConnection.addDest(it) } - ucConnection.commit() - lhsPortIndex += 1 - } else if (rhsPort.channelsLeft() < lhsPort.channelsLeft()) { - // If we have more channels left in the lhs variable, we dont complete the downstreamSet yet, - // we move to the next rhsChannel. Only if this was the very last rhs variable do we commit. - val numRhsChannelsToAdd = rhsPort.channelsLeft() - val rhsChannelsToAdd = rhsPort.takeRemainingChannels() - val lhsChannelsToAdd = lhsPort.takeChannels(numRhsChannelsToAdd) - lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach { ucConnection.addDest(it) } - rhsPortIndex += 1 - if (rhsPortIndex >= rhsPorts.size) { - ucConnection.commit() - } - } else { - // Channels are same size - lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()) - .forEach { ucConnection.addDest(it) } - ucConnection.commit() - rhsPortIndex += 1 - lhsPortIndex += 1 - } - - // If we are out of lhs variables, but not rhs, then there should be an iterated connection. - // We handle it by resetting the lhsChannels variable and index and continuing until - // we have been thorugh all rhs channels. - if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { - assert(conn.isIterated) - lhsPorts = conn.leftPorts.map { UcPort(it) } - lhsPortIndex = 0 - } - } - } - - /** Finds an existing GroupedConnection from srcVarRef with matchin connection properties (physical and delay). */ - fun findExistingGroupedConnection(srcVarRef: VarRef, dstVarRef: VarRef, conn: Connection): UcGroupedConnection? { - if (conn.isFederated) { - return connections.find { c -> - c.srcVarRef == srcVarRef && c.isPhysical == conn.isPhysical && c.delay == conn.delay.orNever().toCCode() - } - } else { - return connections.find { c -> - c.srcVarRef == srcVarRef && c.dstInst == dstVarRef.container && c.isPhysical == conn.isPhysical && c.delay == conn.delay.orNever() - .toCCode() - } - } - } - - /** Finds an existing grouped connection, or creates a new.*/ - fun getOrCreateNewGroupedConnection(srcVarRef: VarRef, dstVarRef: VarRef, conn: Connection): UcGroupedConnection { - var res = findExistingGroupedConnection(srcVarRef, dstVarRef, conn) - if (res == null) { - res = UcGroupedConnection(srcVarRef, dstVarRef, conn, connections.size) - connections.add(res) - } - return res - } - - /** Find the number of grouped connections coming out of a particular port. This is needed to know how many - * Connection pointers to allocated on the self-struct of a reactor containing another reactor with an output port. */ - fun findNumGroupedConnectionsFromPort(srcInst: Instantiation?, srcPort: Port) = - connections.filter { c -> c.srcPort == srcPort && c.srcInst == srcInst }.size - - fun getFederatedConnectionBundles(): List { - val res = mutableListOf() - val connectionsGroupedByBundle: Map, List> = - connections.groupBy { Pair(it.srcInst!!, it.dstInst!!) } - - for ((key, value) in connectionsGroupedByBundle) { - val bundle = UcFederatedConnectionBundle(key.first, key.second, value) - res.add(bundle) - } - return res - } -} +// +//// A class for maintaining all the runtime UcConnections within a reactor. +//class UcConnections() { +// val connections = mutableListOf(); +// +// // Parse a connection and update the list of UcGroupedConnection. This is non-trivial since a single variable +// // in the connection can have several banks and multiports. In a multi-connection, we might connect some channels +// // to one variable and others to another. +// fun addConnection(conn: Connection) { +// // First translate the variables into our UcPort which also has information of channels (banks x multiports) +// val rhsPorts = conn.rightPorts.map { UcPort(it) } +// var rhsPortIndex = 0 +// var lhsPorts = conn.leftPorts.map { UcPort(it) } +// var lhsPortIndex = 0 +// +// // Keep parsing out connections until we are out of right-hand-side (rhs) ports +// while (rhsPortIndex < rhsPorts.size) { +// // First get the current lhs and rhs port and UcGroupedConnection that we are working with +// val lhsPort = lhsPorts.get(lhsPortIndex) +// val rhsPort = rhsPorts.get(rhsPortIndex) +// val ucConnection = getOrCreateNewGroupedConnection(lhsPort.varRef, rhsPort.varRef, conn) +// +// if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { +// // If we have more channels left in the rhs variable, then we "complete" a downstreamSet fo +// // the lhs, commit it, and move to the next lhs variable +// val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) +// val lhsChannelsToAdd = lhsPort.takeRemainingChannels() +// lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach { ucConnection.addDest(it) } +// ucConnection.commit() +// lhsPortIndex += 1 +// } else if (rhsPort.channelsLeft() < lhsPort.channelsLeft()) { +// // If we have more channels left in the lhs variable, we dont complete the downstreamSet yet, +// // we move to the next rhsChannel. Only if this was the very last rhs variable do we commit. +// val numRhsChannelsToAdd = rhsPort.channelsLeft() +// val rhsChannelsToAdd = rhsPort.takeRemainingChannels() +// val lhsChannelsToAdd = lhsPort.takeChannels(numRhsChannelsToAdd) +// lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach { ucConnection.addDest(it) } +// rhsPortIndex += 1 +// if (rhsPortIndex >= rhsPorts.size) { +// ucConnection.commit() +// } +// } else { +// // Channels are same size +// lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()) +// .forEach { ucConnection.addDest(it) } +// ucConnection.commit() +// rhsPortIndex += 1 +// lhsPortIndex += 1 +// } +// +// // If we are out of lhs variables, but not rhs, then there should be an iterated connection. +// // We handle it by resetting the lhsChannels variable and index and continuing until +// // we have been thorugh all rhs channels. +// if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { +// assert(conn.isIterated) +// lhsPorts = conn.leftPorts.map { UcPort(it) } +// lhsPortIndex = 0 +// } +// } +// } +// +// /** Finds an existing GroupedConnection from srcVarRef with matchin connection properties (physical and delay). */ +// fun findExistingGroupedConnection(srcVarRef: VarRef, dstVarRef: VarRef, conn: Connection): UcGroupedConnection2? { +// if (conn.isFederated) { +// return connections.find { c -> +// c.srcVarRef == srcVarRef && c.isPhysical == conn.isPhysical && c.delay == conn.delay.orNever().toCCode() +// } +// } else { +// return connections.find { c -> +// c.srcVarRef == srcVarRef && c.dstInst == dstVarRef.container && c.isPhysical == conn.isPhysical && c.delay == conn.delay.orNever() +// .toCCode() +// } +// } +// } +// +// /** Finds an existing grouped connection, or creates a new.*/ +// fun getOrCreateNewGroupedConnection(srcVarRef: VarRef, dstVarRef: VarRef, conn: Connection): UcGroupedConnection2 { +// var res = findExistingGroupedConnection(srcVarRef, dstVarRef, conn) +// if (res == null) { +// res = UcGroupedConnection2(srcVarRef, dstVarRef, conn, connections.size) +// connections.add(res) +// } +// return res +// } +// +// /** Find the number of grouped connections coming out of a particular port. This is needed to know how many +// * Connection pointers to allocated on the self-struct of a reactor containing another reactor with an output port. */ +// fun findNumGroupedConnectionsFromPort(srcInst: Instantiation?, srcPort: Port) = +// connections.filter { c -> c.srcPort == srcPort && c.srcInst == srcInst }.size +// +// fun getFederatedConnectionBundles(): List { +// val res = mutableListOf() +// val connectionsGroupedByBundle: Map, List> = +// connections.groupBy { Pair(it.srcInst!!, it.dstInst!!) } +// +// for ((key, value) in connectionsGroupedByBundle) { +// val bundle = UcFederatedConnectionBundle(key.first, key.second, value) +// res.add(bundle) +// } +// return res +// } +//} class UcConnectionGenerator(private val reactor: Reactor, private val federate: Instantiation?) { - private val ucConnections = UcConnections() + private val ucGroupedConnections: List private val ucFederatedConnectionBundles: List init { - reactor.allConnections.forEach { ucConnections.addConnection(it) } - if (federate != null) { - ucConnections.connections.removeIf { it.srcInst != federate && it.dstInst != federate } - ucFederatedConnectionBundles = ucConnections.getFederatedConnectionBundles() - } else { - ucFederatedConnectionBundles = emptyList() - } + val channels = mutableListOf() + reactor.allConnections.forEach { channels.addAll(UcConnectionChannel.parseConnectionChannels(it)) } + ucGroupedConnections = UcGroupedConnection2.groupConnections(channels) + + ucFederatedConnectionBundles = emptyList() } companion object { @@ -243,59 +358,65 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: fun getNumFederatedConnectionBundles() = ucFederatedConnectionBundles.size fun getNumConnectionsFromPort(instantiation: Instantiation?, port: Port): Int { - return ucConnections.findNumGroupedConnectionsFromPort(instantiation, port) + var count = 0 + for (groupedConn in ucGroupedConnections) { + if (groupedConn.srcInst == instantiation && groupedConn.srcPort == port) { + count += 1 + } + } + return count } - private fun generateLogicalSelfStruct(conn: UcGroupedConnection) = + private fun generateLogicalSelfStruct(conn: UcGroupedConnection2) = "LF_DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()});" - private fun generateLogicalCtor(conn: UcGroupedConnection) = + private fun generateLogicalCtor(conn: UcGroupedConnection2) = "LF_DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()});" - private fun generateDelayedSelfStruct(conn: UcGroupedConnection) = + private fun generateDelayedSelfStruct(conn: UcGroupedConnection2) = "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay});" - private fun generateDelayedCtor(conn: UcGroupedConnection) = + private fun generateDelayedCtor(conn: UcGroupedConnection2) = "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" - private fun generateFederatedInputSelfStruct(conn: UcGroupedConnection) = + private fun generateFederatedInputSelfStruct(conn: UcGroupedConnection2) = "LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" - private fun generateFederatedInputCtor(conn: UcGroupedConnection) = + private fun generateFederatedInputCtor(conn: UcGroupedConnection2) = "LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" - private fun generateFederatedOutputSelfStruct(conn: UcGroupedConnection) = + private fun generateFederatedOutputSelfStruct(conn: UcGroupedConnection2) = "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()});" - private fun generateFederatedOutputCtor(conn: UcGroupedConnection) = + private fun generateFederatedOutputCtor(conn: UcGroupedConnection2) = "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()});" - private fun generateFederatedConnectionSelfStruct(conn: UcGroupedConnection) = + private fun generateFederatedConnectionSelfStruct(conn: UcGroupedConnection2) = if (conn.srcInst == federate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct(conn) - private fun generateFederatedConnectionCtor(conn: UcGroupedConnection) = + private fun generateFederatedConnectionCtor(conn: UcGroupedConnection2) = if (conn.srcInst == federate) generateFederatedOutputCtor(conn) else generateFederatedInputCtor(conn) - private fun generateFederatedOutputInstance(conn: UcGroupedConnection) = + private fun generateFederatedOutputInstance(conn: UcGroupedConnection2) = "LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.uniqueName});" - private fun generateFederatedInputInstance(conn: UcGroupedConnection) = + private fun generateFederatedInputInstance(conn: UcGroupedConnection2) = "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.uniqueName});" - private fun generateFederatedConnectionInstance(conn: UcGroupedConnection) = + private fun generateFederatedConnectionInstance(conn: UcGroupedConnection2) = if (conn.srcInst == federate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance(conn) - private fun generateInitializeFederatedOutput(conn: UcGroupedConnection) = + private fun generateInitializeFederatedOutput(conn: UcGroupedConnection2) = "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.uniqueName}, ${conn.serializeFunc});" - private fun generateInitializeFederatedInput(conn: UcGroupedConnection) = + private fun generateInitializeFederatedInput(conn: UcGroupedConnection2) = "LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(${reactor.codeType}, ${conn.uniqueName}, ${conn.deserializeFunc});" - private fun generateInitializeFederatedConnection(conn: UcGroupedConnection) = + private fun generateInitializeFederatedConnection(conn: UcGroupedConnection2) = if (conn.srcInst == federate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput(conn) - private fun generateReactorCtorCode(conn: UcGroupedConnection) = with(PrependOperator) { + private fun generateReactorCtorCode(conn: UcGroupedConnection2) = with(PrependOperator) { """ |${if (conn.isLogical) "LF_INITIALIZE_LOGICAL_CONNECTION(" else "LF_INITIALIZE_DELAYED_CONNECTION("}${reactor.codeType}, ${conn.uniqueName}, ${conn.bankWidth}, ${conn.portWidth}); """.trimMargin() @@ -304,29 +425,25 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: private fun generateFederateCtorCode(conn: UcFederatedConnectionBundle) = "LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(${conn.src.codeTypeFederate}, ${conn.dst.codeTypeFederate});" - private fun generateConnectChannel(conn: UcGroupedConnection, channels: Pair) = + private fun generateConnectChannel(groupedConn: UcGroupedConnection2, channel: UcConnectionChannel) = with(PrependOperator) { - """|lf_connect((Connection *) &self->${conn.uniqueName}[${channels.first.bank_idx}][${channels.first.port_idx}], (Port *) ${channels.first.generateChannelPointer()}, (Port *) ${channels.second.generateChannelPointer()}); + """|lf_connect((Connection *) &self->${groupedConn.uniqueName}[${channel.src.bank_idx}][${channel.src.port_idx}], (Port *) ${channel.src.generateChannelPointer()}, (Port *) ${channel.dest.generateChannelPointer()}); """.trimMargin() } - private fun generateConnectionStatements(conn: UcGroupedConnection) = with(PrependOperator) { - conn.getDests() - .joinToString(separator = "\n") { it.joinToString(separator = "\n") { generateConnectChannel(conn, it) } } + private fun generateConnectionStatements(conn: UcGroupedConnection2) = with(PrependOperator) { + conn.channels + .joinToString(separator = "\n") { generateConnectChannel(conn, it) } } - private fun generateConnectFederateOutputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection) = - conn.getDests().joinWithLn { - it.joinWithLn { - "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}).${conn.uniqueName}, (Port*) &self->${bundle.src.name}[0].${it.first.varRef.name}[0]);" - } + private fun generateConnectFederateOutputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection2) = + conn.channels.joinWithLn { + "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}).${conn.uniqueName}, (Port*) &self->${bundle.src.name}[0].${it.src.varRef.name}[0]);" } - private fun generateConnectFederateInputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection) = - conn.getDests().joinWithLn { - it.joinWithLn { - "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}).${conn.uniqueName}, (Port*) &self->${bundle.dst.name}[0].${it.second.varRef.name}[0]);" - } + private fun generateConnectFederateInputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection2) = + conn.channels.joinWithLn { + "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}).${conn.uniqueName}, (Port*) &self->${bundle.dst.name}[0].${it.dest.varRef.name}[0]);" } @@ -352,29 +469,29 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: ) { generateFederateConnectionStatements(it) } fun generateReactorCtorCodes() = - ucConnections.connections.joinToString( + ucGroupedConnections.joinToString( prefix = "// Initialize connections\n", separator = "\n", postfix = "\n" ) { generateReactorCtorCode(it) } + - ucConnections.connections.joinToString( + ucGroupedConnections.joinToString( prefix = "// Do connections \n", separator = "\n", postfix = "\n" ) { generateConnectionStatements(it) } - fun generateCtors() = ucConnections.connections.joinToString( + fun generateCtors() = ucGroupedConnections.joinToString( prefix = "// Connection constructors\n", separator = "\n", postfix = "\n" ) { if (it.isFederated) generateFederatedConnectionCtor(it) - else if (it.conn.isPhysical || it.conn.delay != null) generateDelayedCtor(it) + else if (it.isPhysical || it.delay != null) generateDelayedCtor(it) else generateLogicalCtor(it) } fun generateSelfStructs() = - ucConnections.connections.joinToString(prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { + ucGroupedConnections.joinToString(prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { if (it.isFederated) generateFederatedConnectionSelfStruct(it) else if (it.isLogical) generateLogicalSelfStruct(it) else generateDelayedSelfStruct(it) @@ -441,14 +558,14 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: } fun generateReactorStructFields() = - ucConnections.connections.joinToString(prefix = "// Connections \n", separator = "\n", postfix = "\n") { + ucGroupedConnections.joinToString(prefix = "// Connections \n", separator = "\n", postfix = "\n") { if (it.isLogical) "LF_LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.uniqueName}, ${it.bankWidth}, ${it.portWidth});" else "LF_DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.uniqueName}, ${it.bankWidth}, ${it.portWidth});" } fun getMaxNumPendingEvents(): Int { var res = 0 - for (conn in ucConnections.connections) { + for (conn in ucGroupedConnections) { if (!conn.isLogical) { res += conn.maxNumPendingEvents } From c049c730b6e17134463bf8d5fef8eac204eaf659 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 3 Jan 2025 11:53:07 +0100 Subject: [PATCH 16/65] Various fixes to get all standalone tests to pass again --- .../generator/uc/UcConnectionGenerator.kt | 8 +- .../generator/uc/UcFederatedMainGenerator.kt | 81 +++++++++++++++++++ .../uc/UcFederatedPlatformGenerator.kt | 4 +- .../org/lflang/generator/uc/UcGenerator.kt | 7 +- .../lflang/generator/uc/UcMainGenerator.kt | 30 +------ .../uc/UcStandalonePlatformGenerator.kt | 2 +- 6 files changed, 94 insertions(+), 38 deletions(-) create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 521740d1..b3308138 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -70,13 +70,14 @@ class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Con } open class UcGroupedConnection2(val src: VarRef, val channels: List, val lfConn: Connection, val uid: Int) { - val delay = lfConn.delay + val delay = lfConn.delay.orNever().toCCode() val isPhysical = lfConn.isPhysical val isLogical = lfConn.isLogical val srcInst = src.container val srcPort = src.variable as Port - val isFederated = lfConn.isFederated && channels.first().dest.varRef.container != srcInst + val isDelayed = lfConn.isPhysical || !lfConn.isLogical + val serializeFunc = "serialize_payload_default" val deserializeFunc = "deserialize_payload_default" val bankWidth = srcInst?.width ?: 1 @@ -119,6 +120,7 @@ open class UcGroupedConnection2(val src: VarRef, val channels: Listduration = ${getDuration()}; + | lf_environment.scheduler->keep_alive = ${keepAlive()}; + | lf_environment.scheduler->leader = ${top.instantiations.first() == main}; + | lf_environment.fast_mode = ${fast()}; + | lf_environment.has_async_events = ${main.reactor.inputs.isNotEmpty()}; + | ${main.codeTypeFederate}_ctor(&main_reactor, NULL, &lf_environment); + | lf_environment.net_bundles_size = ${ucConnectionGenerator.getNumFederatedConnectionBundles()}; + | lf_environment.net_bundles = (FederatedConnectionBundle **) &main_reactor._bundles; + | lf_environment.assemble(&lf_environment); + | lf_environment.start(&lf_environment); + | lf_exit(); + |} + """.trimMargin() + } + + fun generateStartHeader() = with(PrependOperator) { + """ + |#ifndef REACTOR_UC_LF_MAIN_H + |#define REACTOR_UC_LF_MAIN_H + | + |void lf_start(void); + | + |#endif + | + """.trimMargin() + } + + fun generateMainSource() = with(PrependOperator) { + """ + |#include "lf_start.h" + |int main(void) { + | lf_start(); + |} + """.trimMargin() + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt index e372e4b2..3a9e8e0e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt @@ -32,13 +32,13 @@ class UcFederatedPlatformGenerator(generator: UcGenerator, val srcGenPath: Path) return; } val runtimePath: Path = Paths.get(reactorUCEnvPath) - val mainGenerator = UcMainGenerator(mainReactor, generator.mainDef, generator.targetConfig, generator.fileConfig) + val mainGenerator = UcFederatedMainGenerator(generator.mainDef, generator.targetConfig, generator.fileConfig) val startSourceFile = Paths.get("lf_start.c") val startHeaderFile = Paths.get("lf_start.h") val mainSourceFile = Paths.get("lf_main.c") - val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateFederatedStartSource()) + val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateStartSource()) val mainCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateMainSource()) ucSources.addAll(listOf(startSourceFile, mainSourceFile)) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index eb161f33..89d3e73a 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -60,9 +60,8 @@ class UcGenerator( fun doGenerateTopLevel(resource: Resource, context: LFGeneratorContext, srcGenPath: Path, platformGenerator: UcPlatformGenerator): GeneratorResult.Status { if (!canGenerate(errorsOccurred(), mainDef, messageReporter, context)) return GeneratorResult.Status.FAILED - // generate all core files - generateFiles(mainDef, srcGenPath, getAllImportedResources(mainDef.eResource())) + generateFiles(mainDef, srcGenPath, getAllImportedResources(resource)) // generate platform specific files platformGenerator.generatePlatformFiles() @@ -96,7 +95,7 @@ class UcGenerator( codeMaps.clear() ucSources.clear() val srcGenPath = fileConfig.srcGenPath.resolve(federate.name) - val res = doGenerateTopLevel(resource, context, srcGenPath, UcFederatedPlatformGenerator(this, srcGenPath)) + val res = doGenerateTopLevel(federate.eResource()!!, context, srcGenPath, UcFederatedPlatformGenerator(this, srcGenPath)) if (res == GeneratorResult.Status.FAILED) { context.unsuccessfulFinish() @@ -159,6 +158,8 @@ class UcGenerator( if (mainDef.isAFederate) { generateFederateFiles(mainDef, srcGenPath) + } else { + generateReactorFiles(mainDef.reactor, srcGenPath) } for (r in resources) { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index f88b7161..ad2c8fd6 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -18,14 +18,12 @@ import org.lflang.toUnixString class UcMainGenerator( private val main: Reactor, - private val mainDef: Instantiation, private val targetConfig: TargetConfig, private val fileConfig: UcFileConfig, ) { - private val top = mainDef.eContainer() as Reactor private val ucParameterGenerator = UcParameterGenerator(main) - private val ucConnectionGenerator = UcConnectionGenerator(top, mainDef) + private val ucConnectionGenerator = UcConnectionGenerator(main, null) fun getDuration() = if (targetConfig.isSet(TimeOutProperty.INSTANCE)) targetConfig.get(TimeOutProperty.INSTANCE).toCCode() else "FOREVER" @@ -55,32 +53,6 @@ class UcMainGenerator( """.trimMargin() } - fun generateFederatedStartSource() = with(PrependOperator) { - """ - |#include "reactor-uc/reactor-uc.h" - |#include "Federate.h" - |static ${mainDef.codeTypeFederate} main_reactor; - |static Environment lf_environment; - |void lf_exit(void) { - | Environment_free(&lf_environment); - |} - |void lf_start(void) { - | Environment_ctor(&lf_environment, (Reactor *)&main_reactor); - | lf_environment.scheduler->duration = ${getDuration()}; - | lf_environment.scheduler->keep_alive = ${keepAlive()}; - | lf_environment.scheduler->leader = ${top.instantiations.first() == mainDef}; - | lf_environment.fast_mode = ${fast()}; - | lf_environment.has_async_events = ${mainDef.reactor.inputs.isNotEmpty()}; - | ${mainDef.codeTypeFederate}_ctor(&main_reactor, NULL, &lf_environment); - | lf_environment.net_bundles_size = ${ucConnectionGenerator.getNumFederatedConnectionBundles()}; - | lf_environment.net_bundles = (FederatedConnectionBundle **) &main_reactor._bundles; - | lf_environment.assemble(&lf_environment); - | lf_environment.start(&lf_environment); - | lf_exit(); - |} - """.trimMargin() - } - fun generateStartHeader() = with(PrependOperator) { """ |#ifndef REACTOR_UC_LF_MAIN_H diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt index 3fb1b9e4..efcc1224 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt @@ -31,7 +31,7 @@ class UcStandalonePlatformGenerator(generator: UcGenerator, val srcGenPath: Path } val runtimePath: Path = Paths.get(reactorUCEnvPath) // generate the main source file (containing main()) - val mainGenerator = UcMainGenerator(mainReactor, generator.mainDef, generator.targetConfig, generator.fileConfig) + val mainGenerator = UcMainGenerator(mainReactor, generator.targetConfig, generator.fileConfig) val startSourceFile = Paths.get("lf_start.c") val startHeaderFile = Paths.get("lf_start.h") From 991264a9114131370aa82d91c4786ac83b94f20c Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 3 Jan 2025 12:41:49 +0100 Subject: [PATCH 17/65] Formatting --- include/reactor-uc/macros.h | 4 +- include/reactor-uc/util.h | 1 - .../generator/uc/UcConnectionGenerator.kt | 98 ++++++------------- src/platform/posix/tcp_ip_channel.c | 2 +- 4 files changed, 31 insertions(+), 74 deletions(-) diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index b89a28de..98749ea0 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -611,7 +611,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; } ReactorName##_##InputName; #define LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(ReactorName, InputName, BufferType, BufferSize, Delay, IsPhysical) \ - void ReactorName##_##InputName##_conn_ctor(ReactorName##_##InputName *self, Reactor *parent) { \ + void ReactorName##_##InputName##_conn_ctor(ReactorName##_##InputName *self, Reactor *parent) { \ FederatedInputConnection_ctor(&self->super, parent, Delay, IsPhysical, (Port **)&self->downstreams, 1, \ (void *)&self->payload_buf, (bool *)&self->payload_used_buf, sizeof(BufferType), \ BufferSize); \ @@ -621,7 +621,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; #define LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(ReactorName, InputName, DeserializeFunc) \ ReactorName##_##InputName##_conn_ctor(&self->InputName, self->super.parent); \ - self->inputs[_inputs_idx] = &self->InputName.super; \ + self->inputs[_inputs_idx] = &self->InputName.super; \ self->deserialize_hooks[_inputs_idx] = DeserializeFunc; \ _inputs_idx++; diff --git a/include/reactor-uc/util.h b/include/reactor-uc/util.h index 0b4ee8ec..1dbb0f30 100644 --- a/include/reactor-uc/util.h +++ b/include/reactor-uc/util.h @@ -10,5 +10,4 @@ void lf_connect_federated_output(Connection *connection, Port *output); void lf_connect_federated_input(Connection *connection, Port *input); - #endif \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index b3308138..5a09bbe1 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -5,7 +5,6 @@ import org.lflang.generator.PrependOperator import org.lflang.generator.orNever import org.lflang.generator.uc.UcConnectionGenerator.Companion.isFederated import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate -import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.width import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType @@ -16,12 +15,7 @@ class UcFederate(val federate: Instantiation, bankIdx: Int) { val isBank = federate.isBank } -class UcConnection(val src: VarRef) { - -} - class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { - companion object { fun parseConnectionChannels(conn: Connection): List { val res = mutableListOf() @@ -69,7 +63,7 @@ class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Con } } -open class UcGroupedConnection2(val src: VarRef, val channels: List, val lfConn: Connection, val uid: Int) { +class UcGroupedConnection(val src: VarRef, val channels: List, val lfConn: Connection, val uid: Int) { val delay = lfConn.delay.orNever().toCCode() val isPhysical = lfConn.isPhysical val isLogical = lfConn.isLogical @@ -96,8 +90,8 @@ open class UcGroupedConnection2(val src: VarRef, val channels: List): List { - val res = mutableListOf() + fun groupConnections(channels: List): List { + val res = mutableListOf() val channels = HashSet(channels) while (channels.isNotEmpty()) { @@ -118,7 +112,7 @@ open class UcGroupedConnection2(val src: VarRef, val channels: List>>() - private var destsBuffer = mutableListOf>() - val srcInst: Instantiation? = srcVarRef.container - val dstInst: Instantiation? = - dstVarRef.container // FIXME: A little hacky since it only makes sense for FederatedGroupConnections - val srcPort = srcVarRef.variable as Port - val bankWidth = srcInst?.width ?: 1 - val portWidth = srcPort.width - val uniqueName = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" - val maxNumPendingEvents = 16 // FIXME: Must be derived from the program - - val delay = conn.delay.orNever().toCCode() - val isPhysical = conn.isPhysical - val isLogical = !isPhysical && conn.delay == null - val isFederated = srcInst != null && srcInst.isAFederate - val serializeFunc = "serialize_payload_default" - val deserializeFunc = "deserialize_payload_default" - - - fun numDownstreams() = dests.size - - fun getDests() = dests - - fun addDest(channels: Pair) { - destsBuffer.add(channels) - } - - fun commit() { - dests.add(destsBuffer) - destsBuffer = mutableListOf>() - } -} - class UcFederatedConnectionBundle( val src: Instantiation, val dst: Instantiation, - val groupedConnections: List + val groupedConnections: List ) { val channelCodeType = "TcpIpChannel" @@ -341,13 +299,13 @@ class UcPort(val varRef: VarRef) { //} class UcConnectionGenerator(private val reactor: Reactor, private val federate: Instantiation?) { - private val ucGroupedConnections: List + private val ucGroupedConnections: List private val ucFederatedConnectionBundles: List init { val channels = mutableListOf() reactor.allConnections.forEach { channels.addAll(UcConnectionChannel.parseConnectionChannels(it)) } - ucGroupedConnections = UcGroupedConnection2.groupConnections(channels) + ucGroupedConnections = UcGroupedConnection.groupConnections(channels) ucFederatedConnectionBundles = emptyList() } @@ -369,56 +327,56 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: return count } - private fun generateLogicalSelfStruct(conn: UcGroupedConnection2) = + private fun generateLogicalSelfStruct(conn: UcGroupedConnection) = "LF_DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()});" - private fun generateLogicalCtor(conn: UcGroupedConnection2) = + private fun generateLogicalCtor(conn: UcGroupedConnection) = "LF_DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()});" - private fun generateDelayedSelfStruct(conn: UcGroupedConnection2) = + private fun generateDelayedSelfStruct(conn: UcGroupedConnection) = "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay});" - private fun generateDelayedCtor(conn: UcGroupedConnection2) = + private fun generateDelayedCtor(conn: UcGroupedConnection) = "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" - private fun generateFederatedInputSelfStruct(conn: UcGroupedConnection2) = + private fun generateFederatedInputSelfStruct(conn: UcGroupedConnection) = "LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" - private fun generateFederatedInputCtor(conn: UcGroupedConnection2) = + private fun generateFederatedInputCtor(conn: UcGroupedConnection) = "LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" - private fun generateFederatedOutputSelfStruct(conn: UcGroupedConnection2) = + private fun generateFederatedOutputSelfStruct(conn: UcGroupedConnection) = "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()});" - private fun generateFederatedOutputCtor(conn: UcGroupedConnection2) = + private fun generateFederatedOutputCtor(conn: UcGroupedConnection) = "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()});" - private fun generateFederatedConnectionSelfStruct(conn: UcGroupedConnection2) = + private fun generateFederatedConnectionSelfStruct(conn: UcGroupedConnection) = if (conn.srcInst == federate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct(conn) - private fun generateFederatedConnectionCtor(conn: UcGroupedConnection2) = + private fun generateFederatedConnectionCtor(conn: UcGroupedConnection) = if (conn.srcInst == federate) generateFederatedOutputCtor(conn) else generateFederatedInputCtor(conn) - private fun generateFederatedOutputInstance(conn: UcGroupedConnection2) = + private fun generateFederatedOutputInstance(conn: UcGroupedConnection) = "LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.uniqueName});" - private fun generateFederatedInputInstance(conn: UcGroupedConnection2) = + private fun generateFederatedInputInstance(conn: UcGroupedConnection) = "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.uniqueName});" - private fun generateFederatedConnectionInstance(conn: UcGroupedConnection2) = + private fun generateFederatedConnectionInstance(conn: UcGroupedConnection) = if (conn.srcInst == federate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance(conn) - private fun generateInitializeFederatedOutput(conn: UcGroupedConnection2) = + private fun generateInitializeFederatedOutput(conn: UcGroupedConnection) = "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.uniqueName}, ${conn.serializeFunc});" - private fun generateInitializeFederatedInput(conn: UcGroupedConnection2) = + private fun generateInitializeFederatedInput(conn: UcGroupedConnection) = "LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(${reactor.codeType}, ${conn.uniqueName}, ${conn.deserializeFunc});" - private fun generateInitializeFederatedConnection(conn: UcGroupedConnection2) = + private fun generateInitializeFederatedConnection(conn: UcGroupedConnection) = if (conn.srcInst == federate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput(conn) - private fun generateReactorCtorCode(conn: UcGroupedConnection2) = with(PrependOperator) { + private fun generateReactorCtorCode(conn: UcGroupedConnection) = with(PrependOperator) { """ |${if (conn.isLogical) "LF_INITIALIZE_LOGICAL_CONNECTION(" else "LF_INITIALIZE_DELAYED_CONNECTION("}${reactor.codeType}, ${conn.uniqueName}, ${conn.bankWidth}, ${conn.portWidth}); """.trimMargin() @@ -427,23 +385,23 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: private fun generateFederateCtorCode(conn: UcFederatedConnectionBundle) = "LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(${conn.src.codeTypeFederate}, ${conn.dst.codeTypeFederate});" - private fun generateConnectChannel(groupedConn: UcGroupedConnection2, channel: UcConnectionChannel) = + private fun generateConnectChannel(groupedConn: UcGroupedConnection, channel: UcConnectionChannel) = with(PrependOperator) { """|lf_connect((Connection *) &self->${groupedConn.uniqueName}[${channel.src.bank_idx}][${channel.src.port_idx}], (Port *) ${channel.src.generateChannelPointer()}, (Port *) ${channel.dest.generateChannelPointer()}); """.trimMargin() } - private fun generateConnectionStatements(conn: UcGroupedConnection2) = with(PrependOperator) { + private fun generateConnectionStatements(conn: UcGroupedConnection) = with(PrependOperator) { conn.channels .joinToString(separator = "\n") { generateConnectChannel(conn, it) } } - private fun generateConnectFederateOutputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection2) = + private fun generateConnectFederateOutputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection) = conn.channels.joinWithLn { "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}).${conn.uniqueName}, (Port*) &self->${bundle.src.name}[0].${it.src.varRef.name}[0]);" } - private fun generateConnectFederateInputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection2) = + private fun generateConnectFederateInputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection) = conn.channels.joinWithLn { "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}).${conn.uniqueName}, (Port*) &self->${bundle.dst.name}[0].${it.dest.varRef.name}[0]);" } diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 41510b67..b5212f2d 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -316,7 +316,7 @@ static lf_ret_t _TcpIpChannel_receive(NetworkChannel *untyped_self, FederateMess continue; } else if (bytes_read == 0) { // This means the connection was closed. - _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CLOSED); + _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CLOSED); TCP_IP_CHANNEL_DEBUG("Other federate gracefully closed socket"); return LF_ERR; } From 43874dd216fdc829d54a37921f685307fd93b6a5 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 8 Jan 2025 13:06:12 +0100 Subject: [PATCH 18/65] More WIP --- .../org/lflang/generator/LFGenerator.java | 5 +- .../org/lflang/validation/AttributeSpec.java | 2 +- .../generator/uc/UcCmakeFederatedGenerator.kt | 77 +++ .../lflang/generator/uc/UcCmakeGenerator.kt | 18 +- .../generator/uc/UcConnectionGenerator.kt | 470 ++++++++++-------- .../generator/uc/UcFederateGenerator.kt | 36 +- .../uc/UcFederatedLaunchScriptGenerator.kt | 10 +- .../generator/uc/UcFederatedMainGenerator.kt | 23 +- .../uc/UcFederatedPlatformGenerator.kt | 29 +- .../org/lflang/generator/uc/UcGenerator.kt | 305 +++++++++--- .../lflang/generator/uc/UcMakeGenerator.kt | 8 +- .../lflang/generator/uc/UcNetworkChannel.kt | 91 ++++ .../generator/uc/UcReactionGenerator.kt | 3 +- .../lflang/generator/uc/UcReactorGenerator.kt | 30 +- .../uc/UcStandalonePlatformGenerator.kt | 7 +- src/federated.c | 10 +- src/platform/posix/tcp_ip_channel.c | 7 +- src/queues.c | 2 +- src/reaction.c | 14 +- src/schedulers/dynamic/scheduler.c | 1 - test/lf/src/FederatedBank.lf | 40 ++ test/lf/src/FederatedConnection.lf | 3 +- test/lf/src/FederatedLoopbackConnection.lf | 33 ++ 23 files changed, 851 insertions(+), 373 deletions(-) create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt create mode 100644 test/lf/src/FederatedBank.lf create mode 100644 test/lf/src/FederatedLoopbackConnection.lf diff --git a/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java b/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java index 2cf6868c..38d2e729 100644 --- a/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java +++ b/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java @@ -15,9 +15,12 @@ import org.lflang.ast.ASTUtils; import org.lflang.generator.uc.UcFileConfig; import org.lflang.generator.uc.UcGenerator; +import org.lflang.generator.uc.UcNonFederatedGenerator; import org.lflang.scoping.LFGlobalScopeProvider; import org.lflang.target.Target; +import static org.lflang.generator.uc.UcGeneratorKt.createUcGenerator; + /** Generates code from your model files on save. */ public class LFGenerator extends AbstractGenerator { @@ -64,7 +67,7 @@ private GeneratorBase createGenerator(LFGeneratorContext context) { // case CPP -> new CppGenerator(context, scopeProvider); // case TS -> new TSGenerator(context); // case Rust -> new RustGenerator(context, scopeProvider); - case UC -> new UcGenerator(context, scopeProvider); + case UC -> createUcGenerator(context, scopeProvider); }; } diff --git a/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java b/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java index a685e98c..491ff569 100644 --- a/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java +++ b/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java @@ -1,4 +1,4 @@ -/************* + /************* * Copyright (c) 2019-2022, The University of California at Berkeley. * * Redistribution and use in source and binary forms, with or without modification, diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt new file mode 100644 index 00000000..1cb750f6 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt @@ -0,0 +1,77 @@ + +package org.lflang.generator.uc + +import org.lflang.* +import org.lflang.target.TargetConfig +import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate +import org.lflang.lf.Instantiation +import org.lflang.lf.Reactor +import org.lflang.target.property.BuildTypeProperty +import org.lflang.target.property.CmakeIncludeProperty +import org.lflang.target.property.PlatformProperty +import org.lflang.target.property.type.PlatformType +import org.lflang.util.FileUtil +import java.nio.file.Path +import java.time.LocalDateTime +import kotlin.io.path.name +import kotlin.math.max + +class UcCmakeFederatedGenerator(private val federate: UcFederate, private val targetConfig: TargetConfig, private val fileConfig: FileConfig, private val numEvents: Int, private val numReactions: Int) { + private val S = '$' // a little trick to escape the dollar sign with $S + private val platform = targetConfig.get(PlatformProperty.INSTANCE).platform + private val includeFiles = targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { fileConfig.srcPath.resolve(it).toUnixString() } + private val mainTarget = federate.codeType + + + fun generateCmake(sources: List) = + if (platform == PlatformType.Platform.NATIVE) { + generateCmakePosix(sources) + } else { + generateCmakeEmbedded(sources) + } + + fun generateCmakeEmbedded(sources: List) = with(PrependOperator) { + """ + |# This file is generated by LFC. It is meant to be included in + |# an existing CMake project. + | + |set(LFC_GEN_SOURCES + ${" | "..sources.filterNot{it.name == "lf_main.c"}.joinWithLn { "$S{CMAKE_CURRENT_LIST_DIR}/${it.toUnixString()}"}} + |) + |set(LFC_GEN_MAIN "$S{CMAKE_CURRENT_LIST_DIR}/lf_main.c") + |set(REACTOR_UC_PATH $S{CMAKE_CURRENT_LIST_DIR}/reactor-uc) + |set(LFC_GEN_INCLUDE_DIRS $S{CMAKE_CURRENT_LIST_DIR}) + |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") + |set(EVENT_QUEUE_SIZE ${max(numEvents, 1)} CACHE STRING "Size of the event queue") + | + """.trimMargin() + } + + fun generateCmakePosix(sources: List) = with(PrependOperator) { + """ + |cmake_minimum_required(VERSION 3.10) + |project(${mainTarget} LANGUAGES C) + |set(PLATFORM POSIX CACHE STRING "Target platform") + |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") + |set(EVENT_QUEUE_SIZE ${max(numEvents, 1)} CACHE STRING "Size of the event queue") + |set(CMAKE_BUILD_TYPE ${targetConfig.getOrDefault(BuildTypeProperty.INSTANCE)}) + |set(NETWORK_CHANNEL_TCP_POSIX ON CACHE BOOL "Use TcpIpChannel") + | + |set(LF_MAIN_TARGET ${mainTarget}) + |set(SOURCES + ${" | "..sources.joinWithLn { it.toUnixString() }} + |) + |add_executable($S{LF_MAIN_TARGET} $S{SOURCES}) + |install(TARGETS $S{LF_MAIN_TARGET} + | RUNTIME DESTINATION $S{CMAKE_INSTALL_BINDIR} + | OPTIONAL + |) + | + |add_subdirectory(reactor-uc) + |target_link_libraries($S{LF_MAIN_TARGET} PRIVATE reactor-uc) + |target_include_directories($S{LF_MAIN_TARGET} PRIVATE $S{CMAKE_CURRENT_LIST_DIR}) + ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")} + """.trimMargin() + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index ac1c7769..9c3c4977 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -5,8 +5,6 @@ import org.lflang.* import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate -import org.lflang.generator.uc.UcReactorGenerator.Companion.getEventQueueSize -import org.lflang.generator.uc.UcReactorGenerator.Companion.getReactionQueueSize import org.lflang.lf.Instantiation import org.lflang.lf.Reactor import org.lflang.target.property.BuildTypeProperty @@ -19,12 +17,11 @@ import java.time.LocalDateTime import kotlin.io.path.name import kotlin.math.max -class UcCmakeGenerator(private val mainDef: Instantiation, private val targetConfig: TargetConfig, private val fileConfig: FileConfig) { +class UcCmakeGenerator(private val mainDef: Instantiation, private val targetConfig: TargetConfig, private val fileConfig: FileConfig, private val numEvents: Int, private val numReactions: Int) { private val S = '$' // a little trick to escape the dollar sign with $S private val platform = targetConfig.get(PlatformProperty.INSTANCE).platform private val includeFiles = targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { fileConfig.srcPath.resolve(it).toUnixString() } - private val main = mainDef.reactor - private val mainTarget = if (mainDef.isAFederate) "${fileConfig.name}_${mainDef.name}" else fileConfig.name + private val mainTarget = fileConfig.name fun generateCmake(sources: List) = @@ -45,8 +42,8 @@ class UcCmakeGenerator(private val mainDef: Instantiation, private val targetCon |set(LFC_GEN_MAIN "$S{CMAKE_CURRENT_LIST_DIR}/lf_main.c") |set(REACTOR_UC_PATH $S{CMAKE_CURRENT_LIST_DIR}/reactor-uc) |set(LFC_GEN_INCLUDE_DIRS $S{CMAKE_CURRENT_LIST_DIR}) - |set(REACTION_QUEUE_SIZE ${main.getReactionQueueSize()} CACHE STRING "Size of the reaction queue") - |set(EVENT_QUEUE_SIZE ${main.getEventQueueSize()} CACHE STRING "Size of the event queue") + |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") + |set(EVENT_QUEUE_SIZE ${max(numEvents, 1)} CACHE STRING "Size of the event queue") | """.trimMargin() } @@ -56,10 +53,9 @@ class UcCmakeGenerator(private val mainDef: Instantiation, private val targetCon |cmake_minimum_required(VERSION 3.10) |project(${mainTarget} LANGUAGES C) |set(PLATFORM POSIX CACHE STRING "Target platform") - |set(REACTION_QUEUE_SIZE ${max(main.getReactionQueueSize(), 1)} CACHE STRING "Size of the reaction queue") - |set(EVENT_QUEUE_SIZE ${max(main.getEventQueueSize(), 1)} CACHE STRING "Size of the event queue") + |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") + |set(EVENT_QUEUE_SIZE ${max(numEvents, 1)} CACHE STRING "Size of the event queue") |set(CMAKE_BUILD_TYPE ${targetConfig.getOrDefault(BuildTypeProperty.INSTANCE)}) - |${if (mainDef.isAFederate) "set(NETWORK_CHANNEL_TCP_POSIX ON CACHE BOOL \"Use TcpIpChannel\")" else ""} | |set(LF_MAIN_TARGET ${mainTarget}) |set(SOURCES @@ -71,7 +67,7 @@ class UcCmakeGenerator(private val mainDef: Instantiation, private val targetCon | OPTIONAL |) | - |add_subdirectory(reactor-uc) + |add_subdirectory(reactor-uc $S{CMAKE_CURRENT_LIST_DIR}) |target_link_libraries($S{LF_MAIN_TARGET} PRIVATE reactor-uc) |target_include_directories($S{LF_MAIN_TARGET} PRIVATE $S{CMAKE_CURRENT_LIST_DIR}) ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 5a09bbe1..3d126920 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -3,19 +3,39 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.generator.orNever -import org.lflang.generator.uc.UcConnectionGenerator.Companion.isFederated +import org.lflang.generator.uc.UcConnectionGenerator.Companion.networkChannelType import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate +import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.width import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.lf.* +class UcFederate(val inst: Instantiation, val bankIdx: Int) { + val isBank = inst.isBank + private val interfaces = mutableListOf() -class UcFederate(val federate: Instantiation, bankIdx: Int) { - val isBank = federate.isBank + val codeType = if (isBank) "${inst.codeTypeFederate}_${bankIdx}" else inst.codeTypeFederate + + fun addInterface(iface: UcNetworkInterface) { + interfaces.add(iface) + } + + fun getInterface(ifaceType: NetworkChannelType): UcNetworkInterface = + interfaces.find{ it.type == ifaceType }!! + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is UcFederate) return false + + val sameInst = inst == other.inst + val sameBank = bankIdx == other.bankIdx + return if (isBank) sameInst && sameBank else sameInst + } } class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { + val isFederated = (src.varRef.container != null) && (src.varRef.container.isAFederate) && (dest.varRef.container != null) && dest.varRef.container.isAFederate && (src.varRef.container != dest.varRef.container) companion object { fun parseConnectionChannels(conn: Connection): List { val res = mutableListOf() @@ -63,82 +83,74 @@ class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Con } } -class UcGroupedConnection(val src: VarRef, val channels: List, val lfConn: Connection, val uid: Int) { +open class UcGroupedConnection( + val src: VarRef, + val channels: List, + val lfConn: Connection, + uid: Int +) { val delay = lfConn.delay.orNever().toCCode() val isPhysical = lfConn.isPhysical - val isLogical = lfConn.isLogical + val isLogical = !lfConn.isPhysical && lfConn.delay == null val srcInst = src.container val srcPort = src.variable as Port - val isFederated = lfConn.isFederated && channels.first().dest.varRef.container != srcInst - val isDelayed = lfConn.isPhysical || !lfConn.isLogical + val isDelayed = lfConn.isPhysical || !isLogical // We define physical connections as delayed. - val serializeFunc = "serialize_payload_default" - val deserializeFunc = "deserialize_payload_default" val bankWidth = srcInst?.width ?: 1 val portWidth = srcPort.width val uniqueName = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" val numDownstreams = { - val frequencyMap = channels.groupingBy { Pair(it.src.port_idx, it.src.bank_idx)}.eachCount() - frequencyMap.values.maxOrNull() ?:0 + val frequencyMap = channels.groupingBy { Pair(it.src.port_idx, it.src.bank_idx) }.eachCount() + frequencyMap.values.maxOrNull() ?: 0 } val maxNumPendingEvents = 16 // FIXME: Must be derived from the program - - - companion object { - private val Connection.delayString - get(): String = this.delay.orNever().toCCode() - private val Connection.isLogical - get(): Boolean = !this.isPhysical && this.delay == null - - fun groupConnections(channels: List): List { - val res = mutableListOf() - val channels = HashSet(channels) - - while (channels.isNotEmpty()) { - val c = channels.first()!! - val others = if (c.conn.isFederated) { - channels.filter { - it.conn.delayString == c.conn.delayString && - it.conn.isPhysical == c.conn.isPhysical && - it.dest.varRef == c.dest.varRef - it.src.varRef == c.src.varRef - } - } else { - channels.filter { - it.conn.delayString == c.conn.delayString && - it.conn.isPhysical == c.conn.isPhysical && - it.src.varRef == c.src.varRef - } - } - - val groupedChannels = others.plus(c) - val groupedConnection = UcGroupedConnection(c.src.varRef, groupedChannels, c.conn, res.size) - res.add(groupedConnection) - channels.removeAll(groupedChannels) - } - return res - } - } } -class UcFederatedConnectionBundle2(val src: UcFederate, val dest: UcFederate, val netChannel: UcNetworkChannel) { -} +class UcFederatedGroupedConnection( + src: VarRef, + channels: List, + lfConn: Connection, + uid: Int, + val srcFed: UcFederate, + val destFed: UcFederate, +) : UcGroupedConnection(src, channels, lfConn, uid) { + val serializeFunc = "serialize_payload_default" + val deserializeFunc = "deserialize_payload_default" +} class UcFederatedConnectionBundle( - val src: Instantiation, - val dst: Instantiation, - val groupedConnections: List + val src: UcFederate, + val dest: UcFederate, + val groupedConnections: List ) { - val channelCodeType = "TcpIpChannel" + val networkChannel: UcNetworkChannel - fun numInputs(federate: Instantiation) = groupedConnections.count { it.channels.first().dest.varRef.container == federate } + init { + val conn = groupedConnections.first().lfConn + val srcIf = src.getInterface(conn.networkChannelType) + val destIf = dest.getInterface(conn.networkChannelType) + + networkChannel = + when (conn.networkChannelType) { + NetworkChannelType.TcpIp -> { + val srcEp = (srcIf as UcTcpIpInterface).createEndpoint(null) + val destEp = (destIf as UcTcpIpInterface).createEndpoint(null) + UcTcpIpChannel(srcEp, destEp) + } + } + } - fun numOutputs(federate: Instantiation) = groupedConnections.count { it.srcInst == federate } + fun numOutputs(federate: UcFederate) = groupedConnections.count { it.srcFed == federate } - fun generateChannelCtor(federate: Instantiation) = - "TcpIpChannel_ctor(&self->channel, parent->env, \"127.0.0.1\", 10000 + ${groupedConnections.first().uid}, AF_INET, ${(federate == src).toString()});" + fun numInputs(federate: UcFederate) = groupedConnections.count { it.destFed == federate } + fun generateNetworkChannelCtor(federate: UcFederate): String = + if (federate == src) { + networkChannel.generateChannelCtorSrc() + } else { + networkChannel.generateChannelCtorDest() + } } // Convenience class around a port variable reference. It is used to encapsulate the management of multi-connections @@ -195,135 +207,169 @@ class UcPort(val varRef: VarRef) { fun channelsLeft(): Int = channels.size } -// -//// A class for maintaining all the runtime UcConnections within a reactor. -//class UcConnections() { -// val connections = mutableListOf(); -// -// // Parse a connection and update the list of UcGroupedConnection. This is non-trivial since a single variable -// // in the connection can have several banks and multiports. In a multi-connection, we might connect some channels -// // to one variable and others to another. -// fun addConnection(conn: Connection) { -// // First translate the variables into our UcPort which also has information of channels (banks x multiports) -// val rhsPorts = conn.rightPorts.map { UcPort(it) } -// var rhsPortIndex = 0 -// var lhsPorts = conn.leftPorts.map { UcPort(it) } -// var lhsPortIndex = 0 -// -// // Keep parsing out connections until we are out of right-hand-side (rhs) ports -// while (rhsPortIndex < rhsPorts.size) { -// // First get the current lhs and rhs port and UcGroupedConnection that we are working with -// val lhsPort = lhsPorts.get(lhsPortIndex) -// val rhsPort = rhsPorts.get(rhsPortIndex) -// val ucConnection = getOrCreateNewGroupedConnection(lhsPort.varRef, rhsPort.varRef, conn) -// -// if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { -// // If we have more channels left in the rhs variable, then we "complete" a downstreamSet fo -// // the lhs, commit it, and move to the next lhs variable -// val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) -// val lhsChannelsToAdd = lhsPort.takeRemainingChannels() -// lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach { ucConnection.addDest(it) } -// ucConnection.commit() -// lhsPortIndex += 1 -// } else if (rhsPort.channelsLeft() < lhsPort.channelsLeft()) { -// // If we have more channels left in the lhs variable, we dont complete the downstreamSet yet, -// // we move to the next rhsChannel. Only if this was the very last rhs variable do we commit. -// val numRhsChannelsToAdd = rhsPort.channelsLeft() -// val rhsChannelsToAdd = rhsPort.takeRemainingChannels() -// val lhsChannelsToAdd = lhsPort.takeChannels(numRhsChannelsToAdd) -// lhsChannelsToAdd.zip(rhsChannelsToAdd).forEach { ucConnection.addDest(it) } -// rhsPortIndex += 1 -// if (rhsPortIndex >= rhsPorts.size) { -// ucConnection.commit() -// } -// } else { -// // Channels are same size -// lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()) -// .forEach { ucConnection.addDest(it) } -// ucConnection.commit() -// rhsPortIndex += 1 -// lhsPortIndex += 1 -// } -// -// // If we are out of lhs variables, but not rhs, then there should be an iterated connection. -// // We handle it by resetting the lhsChannels variable and index and continuing until -// // we have been thorugh all rhs channels. -// if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { -// assert(conn.isIterated) -// lhsPorts = conn.leftPorts.map { UcPort(it) } -// lhsPortIndex = 0 -// } -// } -// } -// -// /** Finds an existing GroupedConnection from srcVarRef with matchin connection properties (physical and delay). */ -// fun findExistingGroupedConnection(srcVarRef: VarRef, dstVarRef: VarRef, conn: Connection): UcGroupedConnection2? { -// if (conn.isFederated) { -// return connections.find { c -> -// c.srcVarRef == srcVarRef && c.isPhysical == conn.isPhysical && c.delay == conn.delay.orNever().toCCode() -// } -// } else { -// return connections.find { c -> -// c.srcVarRef == srcVarRef && c.dstInst == dstVarRef.container && c.isPhysical == conn.isPhysical && c.delay == conn.delay.orNever() -// .toCCode() -// } -// } -// } -// -// /** Finds an existing grouped connection, or creates a new.*/ -// fun getOrCreateNewGroupedConnection(srcVarRef: VarRef, dstVarRef: VarRef, conn: Connection): UcGroupedConnection2 { -// var res = findExistingGroupedConnection(srcVarRef, dstVarRef, conn) -// if (res == null) { -// res = UcGroupedConnection2(srcVarRef, dstVarRef, conn, connections.size) -// connections.add(res) -// } -// return res -// } -// -// /** Find the number of grouped connections coming out of a particular port. This is needed to know how many -// * Connection pointers to allocated on the self-struct of a reactor containing another reactor with an output port. */ -// fun findNumGroupedConnectionsFromPort(srcInst: Instantiation?, srcPort: Port) = -// connections.filter { c -> c.srcPort == srcPort && c.srcInst == srcInst }.size -// -// fun getFederatedConnectionBundles(): List { -// val res = mutableListOf() -// val connectionsGroupedByBundle: Map, List> = -// connections.groupBy { Pair(it.srcInst!!, it.dstInst!!) } -// -// for ((key, value) in connectionsGroupedByBundle) { -// val bundle = UcFederatedConnectionBundle(key.first, key.second, value) -// res.add(bundle) -// } -// return res -// } -//} - -class UcConnectionGenerator(private val reactor: Reactor, private val federate: Instantiation?) { - private val ucGroupedConnections: List - private val ucFederatedConnectionBundles: List +class UcConnectionGenerator(private val reactor: Reactor, private val federate: UcFederate?) { + + private val nonFederatedConnections: List + private val federatedConnectionBundles: List + private val isFederated = federate != null + + private fun groupConnections(channels: List): List { + val res = mutableListOf() + val channels = HashSet(channels) + + while (channels.isNotEmpty()) { + val c = channels.first()!! + + if (c.isFederated) { + val grouped = + channels.filter { + it.conn.delayString == c.conn.delayString && + it.conn.isPhysical == c.conn.isPhysical && + it.dest.varRef == c.dest.varRef && + it.src.varRef == c.src.varRef && + it.src.bank_idx == c.src.bank_idx && + it.dest.bank_idx == c.dest.bank_idx && + it.conn.networkChannelType == c.conn.networkChannelType + } + + val srcFed = allFederates.find {it == UcFederate(c.src.varRef.container, c.src.bank_idx)}!! + val destFed = allFederates.find {it == UcFederate(c.dest.varRef.container, c.dest.bank_idx)}!! + val groupedConnection = UcFederatedGroupedConnection( + c.src.varRef, + grouped, + c.conn, + res.size, + srcFed, + destFed, + ) + + res.add(groupedConnection) + channels.removeAll(grouped) + + } else { + val grouped = + channels.filter { + it.conn.delayString == c.conn.delayString && + it.conn.isPhysical == c.conn.isPhysical && + it.src.varRef == c.src.varRef + } + + val groupedConnection = UcGroupedConnection(c.src.varRef, grouped, c.conn, res.size) + res.add(groupedConnection) + channels.removeAll(grouped) + } + } + return res + } + companion object { + private val Connection.delayString + get(): String = this.delay.orNever().toCCode() + + val Connection.networkChannelType + get(): NetworkChannelType = NetworkChannelType.TcpIp + + private var allFederatedConnectionBundles: List = emptyList() + private var allFederates: Set = emptySet() + + + private fun createAllFederates(top: Reactor) { + val feds = mutableSetOf() + for (inst in top.allInstantiations) { + for (bankIdx in 0..inst.width) { + val fed = UcFederate(inst, bankIdx) + fed.addInterface(UcTcpIpInterface()) + feds.add(fed) + } + } + allFederates = feds + } + + private fun createFederatedConnectionBundles(groupedConnections: List) { + val groupedSet = HashSet(groupedConnections) + val bundles = mutableListOf() + + while (groupedSet.isNotEmpty()) { + val g = groupedSet.first()!! + val toRemove = mutableListOf(g) + when (g) { + is UcFederatedGroupedConnection -> { + val group = groupedSet.filterIsInstance().filter { + it.srcFed == g.srcFed && + it.destFed == g.destFed + } + + bundles.add( + UcFederatedConnectionBundle( + g.srcFed, g.destFed, group + ) + ) + + toRemove.addAll(group) + } + } + groupedSet.removeAll(toRemove) + } + allFederatedConnectionBundles = bundles + } + } + init { + if (isFederated && allFederates.isEmpty()) { + createAllFederates(reactor) + } + + // Only parse out federated connection bundles once for the very first federate val channels = mutableListOf() reactor.allConnections.forEach { channels.addAll(UcConnectionChannel.parseConnectionChannels(it)) } - ucGroupedConnections = UcGroupedConnection.groupConnections(channels) + val grouped = groupConnections(channels) + nonFederatedConnections = mutableListOf() + federatedConnectionBundles = mutableListOf() + + if (isFederated) { + // Only parse out federated connection bundles once for the very first federate + if (allFederatedConnectionBundles.isEmpty()) { + createFederatedConnectionBundles(grouped) + } - ucFederatedConnectionBundles = emptyList() + // Filter out the relevant bundles for this federate + federatedConnectionBundles.addAll( + allFederatedConnectionBundles.filter { it.src == federate || it.dest == federate } + ) + // Add all non-federated conncetions (e.g. a loopback connection) + // FIXME: How can we handle banks here? + nonFederatedConnections.addAll( + grouped + .filterNot{ it is UcFederatedGroupedConnection} + .filter{it.srcInst == federate!!.inst} + ) + } else { + nonFederatedConnections.addAll(grouped) + } } - companion object { - val Connection.isFederated - get(): Boolean = (this.eContainer() as Reactor).isFederated - } - fun getNumFederatedConnectionBundles() = ucFederatedConnectionBundles.size + fun getNumFederatedConnectionBundles() = federatedConnectionBundles.size fun getNumConnectionsFromPort(instantiation: Instantiation?, port: Port): Int { var count = 0 - for (groupedConn in ucGroupedConnections) { + // Find all outgoing non-federated grouped connections from this port + for (groupedConn in nonFederatedConnections) { if (groupedConn.srcInst == instantiation && groupedConn.srcPort == port) { count += 1 } } + + // Find all outgoing federated grouped connections from this port. + for (federatedConnectionBundle in federatedConnectionBundles) { + if (federatedConnectionBundle.src == federate) { + for (groupedConn in federatedConnectionBundle.groupedConnections) { + if (groupedConn.srcInst == instantiation && groupedConn.srcPort == port) { + count += 1 + } + } + } + } return count } @@ -351,11 +397,11 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: private fun generateFederatedOutputCtor(conn: UcGroupedConnection) = "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()});" - private fun generateFederatedConnectionSelfStruct(conn: UcGroupedConnection) = - if (conn.srcInst == federate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct(conn) + private fun generateFederatedConnectionSelfStruct(conn: UcFederatedGroupedConnection) = + if (conn.srcFed == federate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct(conn) - private fun generateFederatedConnectionCtor(conn: UcGroupedConnection) = - if (conn.srcInst == federate) generateFederatedOutputCtor(conn) else generateFederatedInputCtor(conn) + private fun generateFederatedConnectionCtor(conn: UcFederatedGroupedConnection) = + if (conn.srcFed == federate) generateFederatedOutputCtor(conn) else generateFederatedInputCtor(conn) private fun generateFederatedOutputInstance(conn: UcGroupedConnection) = "LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.uniqueName});" @@ -363,17 +409,17 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: private fun generateFederatedInputInstance(conn: UcGroupedConnection) = "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.uniqueName});" - private fun generateFederatedConnectionInstance(conn: UcGroupedConnection) = - if (conn.srcInst == federate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance(conn) + private fun generateFederatedConnectionInstance(conn: UcFederatedGroupedConnection) = + if (conn.srcFed == federate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance(conn) - private fun generateInitializeFederatedOutput(conn: UcGroupedConnection) = + private fun generateInitializeFederatedOutput(conn: UcFederatedGroupedConnection) = "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.uniqueName}, ${conn.serializeFunc});" - private fun generateInitializeFederatedInput(conn: UcGroupedConnection) = + private fun generateInitializeFederatedInput(conn: UcFederatedGroupedConnection) = "LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(${reactor.codeType}, ${conn.uniqueName}, ${conn.deserializeFunc});" - private fun generateInitializeFederatedConnection(conn: UcGroupedConnection) = - if (conn.srcInst == federate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput(conn) + private fun generateInitializeFederatedConnection(conn: UcFederatedGroupedConnection) = + if (conn.srcFed == federate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput(conn) private fun generateReactorCtorCode(conn: UcGroupedConnection) = with(PrependOperator) { @@ -383,7 +429,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: }; private fun generateFederateCtorCode(conn: UcFederatedConnectionBundle) = - "LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(${conn.src.codeTypeFederate}, ${conn.dst.codeTypeFederate});" + "LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(${conn.src.codeType}, ${conn.dest.codeType});" private fun generateConnectChannel(groupedConn: UcGroupedConnection, channel: UcConnectionChannel) = with(PrependOperator) { @@ -396,14 +442,17 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: .joinToString(separator = "\n") { generateConnectChannel(conn, it) } } - private fun generateConnectFederateOutputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection) = + private fun generateConnectFederateOutputChannel( + bundle: UcFederatedConnectionBundle, + conn: UcFederatedGroupedConnection + ) = conn.channels.joinWithLn { - "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}).${conn.uniqueName}, (Port*) &self->${bundle.src.name}[0].${it.src.varRef.name}[0]);" + "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.uniqueName}, (Port*) &self->${bundle.src.inst.name}[0].${it.src.varRef.name}[0]);" } private fun generateConnectFederateInputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection) = conn.channels.joinWithLn { - "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}).${conn.uniqueName}, (Port*) &self->${bundle.dst.name}[0].${it.dest.varRef.name}[0]);" + "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.uniqueName}, (Port*) &self->${bundle.dest.inst.name}[0].${it.dest.varRef.name}[0]);" } @@ -417,72 +466,70 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: } fun generateFederateCtorCodes() = - ucFederatedConnectionBundles.joinToString( + federatedConnectionBundles.joinToString( prefix = "// Initialize connection bundles\n", separator = "\n", postfix = "\n" ) { generateFederateCtorCode(it) } + - ucFederatedConnectionBundles.joinToString( + federatedConnectionBundles.joinToString( prefix = "// Do connections \n", separator = "\n", postfix = "\n" ) { generateFederateConnectionStatements(it) } fun generateReactorCtorCodes() = - ucGroupedConnections.joinToString( + nonFederatedConnections.joinToString( prefix = "// Initialize connections\n", separator = "\n", postfix = "\n" ) { generateReactorCtorCode(it) } + - ucGroupedConnections.joinToString( + nonFederatedConnections.joinToString( prefix = "// Do connections \n", separator = "\n", postfix = "\n" ) { generateConnectionStatements(it) } - fun generateCtors() = ucGroupedConnections.joinToString( + fun generateCtors() = nonFederatedConnections.joinToString( prefix = "// Connection constructors\n", separator = "\n", postfix = "\n" ) { - if (it.isFederated) generateFederatedConnectionCtor(it) - else if (it.isDelayed) generateDelayedCtor(it) + if (it.isDelayed) generateDelayedCtor(it) else generateLogicalCtor(it) } fun generateSelfStructs() = - ucGroupedConnections.joinToString(prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { - if (it.isFederated) generateFederatedConnectionSelfStruct(it) - else if (it.isLogical) generateLogicalSelfStruct(it) + nonFederatedConnections.joinToString(prefix = "// Connection structs\n", separator = "\n", postfix = "\n") { + if (it.isLogical) generateLogicalSelfStruct(it) else generateDelayedSelfStruct(it) } - fun generateFederatedConnectionBundleSelfStruct(bundle: UcFederatedConnectionBundle) = with(PrependOperator) { """ |typedef struct { | FederatedConnectionBundle super; - ${" | "..bundle.channelCodeType} channel; + ${" | "..bundle.networkChannel.codeType} channel; ${" | "..bundle.groupedConnections.joinWithLn { generateFederatedConnectionInstance(it) }} | LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(${bundle.numInputs(federate!!)}, ${ bundle.numOutputs( - federate!! + federate !! ) }); - |} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}); + |} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(${bundle.src.codeType}, ${bundle.dest.codeType}); + | """.trimMargin() } fun generateFederatedConnectionBundleCtor(bundle: UcFederatedConnectionBundle) = with(PrependOperator) { - """ |LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(${bundle.src.codeTypeFederate}, ${bundle.dst.codeTypeFederate}) { + """ |LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(${bundle.src.codeType}, ${bundle.dest.codeType}) { | LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - | ${bundle.generateChannelCtor(federate!!)} + | ${bundle.generateNetworkChannelCtor(federate!!)} | LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); ${" | "..bundle.groupedConnections.joinWithLn { generateInitializeFederatedConnection(it) }} |} """.trimMargin() } - fun generateFederatedSelfStructs() = ucFederatedConnectionBundles.joinToString( + fun generateFederatedSelfStructs() = federatedConnectionBundles.joinToString( prefix = "// Federated Connections\n", separator = "\n", postfix = "\n" @@ -495,7 +542,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: generateFederatedConnectionBundleSelfStruct(it) } - fun generateFederatedCtors() = ucFederatedConnectionBundles.joinToString( + fun generateFederatedCtors() = federatedConnectionBundles.joinToString( prefix = "// Federated Connections\n", separator = "\n", postfix = "\n" @@ -509,27 +556,34 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: } - fun generateFederateStructFields() = ucFederatedConnectionBundles.joinToString( + fun generateFederateStructFields() = federatedConnectionBundles.joinToString( prefix = "// Federated Connections\n", separator = "\n", postfix = "\n" ) { - "LF_FEDERATED_CONNECTION_BUNDLE_INSTANCE(${it.src.codeTypeFederate}, ${it.dst.codeTypeFederate});" + "LF_FEDERATED_CONNECTION_BUNDLE_INSTANCE(${it.src.codeType}, ${it.dest.codeType});" } fun generateReactorStructFields() = - ucGroupedConnections.joinToString(prefix = "// Connections \n", separator = "\n", postfix = "\n") { + nonFederatedConnections.joinToString(prefix = "// Connections \n", separator = "\n", postfix = "\n") { if (it.isLogical) "LF_LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.uniqueName}, ${it.bankWidth}, ${it.portWidth});" else "LF_DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.uniqueName}, ${it.bankWidth}, ${it.portWidth});" } fun getMaxNumPendingEvents(): Int { var res = 0 - for (conn in ucGroupedConnections) { + for (conn in nonFederatedConnections) { if (!conn.isLogical) { res += conn.maxNumPendingEvents } } + for (bundle in federatedConnectionBundles) { + for (conn in bundle.groupedConnections) { + if (conn.destFed == federate) { + res += conn.maxNumPendingEvents + } + } + } return res } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 9209c30d..6473ab6c 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -1,36 +1,39 @@ package org.lflang.generator.uc -import org.lflang.MessageReporter +import org.lflang.* import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate import org.lflang.lf.* -import org.lflang.reactor -import org.lflang.toUnixString -class UcFederateGenerator(private val federate: Instantiation, private val fileConfig: UcFileConfig, messageReporter: MessageReporter) { - private val container = federate.eContainer() as Reactor - private val reactor = federate.reactor +class UcFederateGenerator(private val federate: UcFederate, private val fileConfig: UcFileConfig, messageReporter: MessageReporter) { + + private val container = federate.inst.eContainer() as Reactor + private val reactor = federate.inst.reactor private val connections = UcConnectionGenerator(container, federate) private val parameters = UcParameterGenerator(container) private val ports = UcPortGenerator(container, connections) private val reactions = UcReactionGenerator(container) private val instances = UcInstanceGenerator(container, parameters, ports, connections, reactions, fileConfig, messageReporter) - private val headerFile = "Federate.h" + private val headerFile = "lf_federate.h" - fun numBundles() = 1 + fun numBundles() = connections.getNumFederatedConnectionBundles() // FIXME: Calculate event queue size for federate... - private val includeGuard = "LFC_GEN_FEDERATE_${federate.name.uppercase()}_H" + private val includeGuard = "LFC_GEN_FEDERATE_${federate.inst.name.uppercase()}_H" + + fun getMaxNumPendingEvents(): Int { + return connections.getMaxNumPendingEvents() + } private fun generateFederateStruct() = with(PrependOperator) { """ |typedef struct { | Reactor super; - ${" | "..instances.generateReactorStructField(federate)} + ${" | "..instances.generateReactorStructField(federate.inst)} + ${" | "..connections.generateReactorStructFields()} ${" | "..connections.generateFederateStructFields()} | LF_FEDERATE_BOOKKEEPING_INSTANCES(${numBundles()}); - |} ${federate.codeTypeFederate}; + |} ${federate.codeType}; | """.trimMargin() } @@ -39,15 +42,16 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC """ |${generateCtorDeclaration()} { | LF_FEDERATE_CTOR_PREAMBLE(); - | LF_REACTOR_CTOR(${federate.codeTypeFederate}); - ${" | "..instances.generateReactorCtorCode(federate)} + | LF_REACTOR_CTOR(${federate.codeType}); + ${" | "..instances.generateReactorCtorCode(federate.inst)} ${" | "..connections.generateFederateCtorCodes()} + ${" | "..connections.generateReactorCtorCodes()} |} | """.trimMargin() } - private fun generateCtorDeclaration() = "LF_REACTOR_CTOR_SIGNATURE(${federate.codeTypeFederate})" + private fun generateCtorDeclaration() = "LF_REACTOR_CTOR_SIGNATURE(${federate.codeType})" fun generateHeader() = with(PrependOperator) { """ @@ -58,6 +62,7 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC | |#include "reactor-uc/platform/posix/tcp_ip_channel.h" ${" |"..connections.generateFederatedSelfStructs()} + ${" |"..connections.generateSelfStructs()} ${" |"..generateFederateStruct()} ${" |"..generateCtorDeclaration()}; |#endif // ${includeGuard} @@ -69,6 +74,7 @@ class UcFederateGenerator(private val federate: Instantiation, private val fileC |#include "${headerFile}" | ${" |"..connections.generateFederatedCtors()} + ${" |"..connections.generateCtors()} ${" |"..generateCtorDefinition()} | """.trimMargin() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt index 82fe2a7b..95a130f1 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt @@ -9,7 +9,7 @@ import kotlin.io.path.name class UcFederatedLaunchScriptGenerator(private val fileConfig: UcFileConfig) { private val S = '$' // a little trick to escape the dollar sign with $S - fun generateLaunchScript(federates: List): String = with(PrependOperator) { + fun generateLaunchScript(federates: List): String = with(PrependOperator) { """ |#!/bin/env bash | |set -m @@ -40,10 +40,10 @@ class UcFederatedLaunchScriptGenerator(private val fileConfig: UcFileConfig) { """.trimMargin() } - fun launchFederate(federate: Instantiation) = with(PrependOperator) { - """ |echo "#### Launching federate ${federate.codeTypeFederate}" - |${fileConfig.binPath}/${federate.codeTypeFederate} - |pids+=$S! + fun launchFederate(federate: UcFederate) = with(PrependOperator) { + """ |echo "#### Launching federate ${federate.codeType}" + |${fileConfig.binPath}/${federate.codeType} & + |pids+=($S!) | """.trimMargin() } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt index c862a1a9..624101f1 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt @@ -2,28 +2,19 @@ package org.lflang.generator.uc import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate -import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType -import org.lflang.inferredType -import org.lflang.lf.Instantiation -import org.lflang.lf.Parameter import org.lflang.lf.Reactor import org.lflang.reactor import org.lflang.target.property.FastProperty import org.lflang.target.property.KeepaliveProperty -import org.lflang.target.property.PlatformProperty import org.lflang.target.property.TimeOutProperty -import org.lflang.target.property.type.PlatformType -import org.lflang.toUnixString class UcFederatedMainGenerator( - private val main: Instantiation, + private val main: UcFederate, private val targetConfig: TargetConfig, private val fileConfig: UcFileConfig, ) { - private val top = main.eContainer() as Reactor - private val ucParameterGenerator = UcParameterGenerator(main.reactor) + private val top = main.inst.eContainer() as Reactor private val ucConnectionGenerator = UcConnectionGenerator(top, main) fun getDuration() = if (targetConfig.isSet(TimeOutProperty.INSTANCE)) targetConfig.get(TimeOutProperty.INSTANCE).toCCode() else "FOREVER" @@ -35,8 +26,8 @@ class UcFederatedMainGenerator( fun generateStartSource() = with(PrependOperator) { """ |#include "reactor-uc/reactor-uc.h" - |#include "Federate.h" - |static ${main.codeTypeFederate} main_reactor; + |#include "lf_federate.h" + |static ${main.codeType} main_reactor; |static Environment lf_environment; |void lf_exit(void) { | Environment_free(&lf_environment); @@ -45,10 +36,10 @@ class UcFederatedMainGenerator( | Environment_ctor(&lf_environment, (Reactor *)&main_reactor); | lf_environment.scheduler->duration = ${getDuration()}; | lf_environment.scheduler->keep_alive = ${keepAlive()}; - | lf_environment.scheduler->leader = ${top.instantiations.first() == main}; + | lf_environment.scheduler->leader = ${top.instantiations.first() == main.inst}; | lf_environment.fast_mode = ${fast()}; - | lf_environment.has_async_events = ${main.reactor.inputs.isNotEmpty()}; - | ${main.codeTypeFederate}_ctor(&main_reactor, NULL, &lf_environment); + | lf_environment.has_async_events = ${main.inst.reactor.inputs.isNotEmpty()}; + | ${main.codeType}_ctor(&main_reactor, NULL, &lf_environment); | lf_environment.net_bundles_size = ${ucConnectionGenerator.getNumFederatedConnectionBundles()}; | lf_environment.net_bundles = (FederatedConnectionBundle **) &main_reactor._bundles; | lf_environment.assemble(&lf_environment); diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt index 3a9e8e0e..2f8fc3a5 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt @@ -2,8 +2,8 @@ package org.lflang.generator.uc import org.lflang.generator.CodeMap import org.lflang.generator.LFGeneratorContext -import org.lflang.generator.PrependOperator import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate +import org.lflang.reactor import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.type.BuildTypeType.BuildType import org.lflang.toUnixString @@ -14,7 +14,7 @@ import java.nio.file.Path import java.nio.file.Paths import kotlin.io.path.createSymbolicLinkPointingTo -class UcFederatedPlatformGenerator(generator: UcGenerator, val srcGenPath: Path) : +class UcFederatedPlatformGenerator(generator: UcFederatedGenerator, private val srcGenPath: Path, private val federate: UcFederate) : UcPlatformGenerator(generator) { companion object { @@ -24,15 +24,16 @@ class UcFederatedPlatformGenerator(generator: UcGenerator, val srcGenPath: Path) } } + val buildPath = srcGenPath.resolve("build") + override fun generatePlatformFiles() { val reactorUCEnvPath = System.getenv("REACTOR_UC_PATH") - // FIXME: Improve this error handling if (reactorUCEnvPath == null) { messageReporter.nowhere().error("REACTOR_UC_PATH environment variable not defined. Do source env.bash in reactor-uc") return; } val runtimePath: Path = Paths.get(reactorUCEnvPath) - val mainGenerator = UcFederatedMainGenerator(generator.mainDef, generator.targetConfig, generator.fileConfig) + val mainGenerator = UcFederatedMainGenerator(federate, generator.targetConfig, generator.fileConfig) val startSourceFile = Paths.get("lf_start.c") val startHeaderFile = Paths.get("lf_start.h") @@ -49,12 +50,14 @@ class UcFederatedPlatformGenerator(generator: UcGenerator, val srcGenPath: Path) FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) FileUtil.writeToFile(mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) - val cmakeGenerator = UcCmakeGenerator(generator.mainDef, targetConfig, generator.fileConfig) - val makeGenerator = UcMakeGenerator(mainReactor, targetConfig, generator.fileConfig) + val numEventsAndReactions = (generator as UcFederatedGenerator).totalNumEventsAndReactionsFederated(federate) + + val cmakeGenerator = UcCmakeFederatedGenerator(federate, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) + val makeGenerator = UcMakeGenerator(federate.inst.reactor, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) FileUtil.writeToFile(cmakeGenerator.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) val launchScriptGenerator = UcFederatedLaunchScriptGenerator(fileConfig) - FileUtil.writeToFile(launchScriptGenerator.generateLaunchScript(generator.getAllFederates()), fileConfig.binPath.resolve(fileConfig.name)) + FileUtil.writeToFile(launchScriptGenerator.generateLaunchScript(generator.getAllUcFederates()), fileConfig.binPath.resolve(fileConfig.name)) fileConfig.binPath.resolve(fileConfig.name).toFile().setExecutable(true) val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); @@ -70,7 +73,7 @@ class UcFederatedPlatformGenerator(generator: UcGenerator, val srcGenPath: Path) override fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean): Boolean { // make sure the build directory exists - Files.createDirectories(fileConfig.buildPath) + Files.createDirectories(buildPath) val version = checkCmakeVersion() var parallelize = true @@ -84,11 +87,11 @@ class UcFederatedPlatformGenerator(generator: UcGenerator, val srcGenPath: Path) if (cmakeReturnCode == 0 && !onlyGenerateBuildFiles) { // If cmake succeeded, run make - val makeCommand = createMakeCommand(fileConfig.buildPath, parallelize, generator.mainDef.codeTypeFederate) + val makeCommand = createMakeCommand(buildPath, parallelize, federate.codeType) val makeReturnCode = UcValidator(fileConfig, messageReporter, codeMaps).run(makeCommand, context.cancelIndicator) var installReturnCode = 0 if (makeReturnCode == 0) { - val installCommand = createMakeCommand(fileConfig.buildPath, parallelize, "install") + val installCommand = createMakeCommand(buildPath, parallelize, "install") installReturnCode = installCommand.run(context.cancelIndicator) if (installReturnCode == 0) { println("SUCCESS (compiling generated C code)") @@ -111,7 +114,7 @@ class UcFederatedPlatformGenerator(generator: UcGenerator, val srcGenPath: Path) private fun checkCmakeVersion(): String? { // get the installed cmake version and make sure it is at least 3.5 - val cmd = commandFactory.createCommand("cmake", listOf("--version"), fileConfig.buildPath) + val cmd = commandFactory.createCommand("cmake", listOf("--version"), buildPath) var version: String? = null if (cmd != null && cmd.run() == 0) { val regex = "\\d+(\\.\\d+)+".toRegex() @@ -135,7 +138,7 @@ class UcFederatedPlatformGenerator(generator: UcGenerator, val srcGenPath: Path) * @return True, if cmake run successfully */ private fun runCmake(context: LFGeneratorContext): Int { - val cmakeCommand = createCmakeCommand(fileConfig.buildPath, fileConfig.outPath) + val cmakeCommand = createCmakeCommand(buildPath, fileConfig.outPath) return cmakeCommand.run(context.cancelIndicator) } @@ -185,7 +188,7 @@ class UcFederatedPlatformGenerator(generator: UcGenerator, val srcGenPath: Path) "-S", sourcesRoot ?: srcGenPath.toUnixString(), "-B", - buildPath.fileName.toString() + buildPath.toString() ) private fun createCmakeCommand( diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index 89d3e73a..a540947f 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -1,12 +1,18 @@ package org.lflang.generator.uc +import org.apache.commons.lang3.tuple.MutablePair +import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.resource.Resource +import org.eclipse.xtext.xbase.lib.IteratorExtensions import org.lflang.allInstantiations +import org.lflang.allReactions import org.lflang.generator.* import org.lflang.generator.GeneratorUtils.canGenerate -import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate +import org.lflang.generator.uc.UcInstanceGenerator.Companion.width +import org.lflang.generator.uc.UcReactorGenerator.Companion.hasStartup import org.lflang.isGeneric import org.lflang.lf.Instantiation +import org.lflang.lf.LfFactory import org.lflang.lf.Reactor import org.lflang.reactor import org.lflang.scoping.LFGlobalScopeProvider @@ -16,12 +22,20 @@ import org.lflang.target.property.type.PlatformType import org.lflang.util.FileUtil import java.nio.file.Path +fun createUcGenerator(context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider): UcGenerator { + val nodes: Iterable = IteratorExtensions.toIterable(context.getFileConfig().resource.getAllContents()); + for (reactor in nodes.filterIsInstance()) { + if (reactor.isFederated) { + return UcFederatedGenerator(context, scopeProvider) + } + } + return UcNonFederatedGenerator(context, scopeProvider) +} + @Suppress("unused") -class UcGenerator( - val context: LFGeneratorContext, - private val scopeProvider: LFGlobalScopeProvider -) : - GeneratorBase(context) { +abstract class UcGenerator( + val context: LFGeneratorContext, protected val scopeProvider: LFGlobalScopeProvider +) : GeneratorBase(context) { // keep a list of all source files we generate val ucSources = mutableListOf() @@ -30,14 +44,50 @@ class UcGenerator( val fileConfig: UcFileConfig = context.fileConfig as UcFileConfig val platform = targetConfig.get(PlatformProperty.INSTANCE) + val maxNumPendingEvents = mutableMapOf() + + + private fun _totalNumEventsAndReactions(inst: Instantiation): Triple { + var numEvents = 0 + var numReactions = 0 + var hasStartup = false + val remaining = mutableListOf() + remaining.addAll(inst.reactor.allInstantiations) + while (remaining.isNotEmpty()) { + val child = remaining.removeFirst() + val childRes = _totalNumEventsAndReactions(child) + + numEvents += childRes.first * child.width + numReactions += childRes.second * child.width + hasStartup = hasStartup or childRes.third + } + numEvents += maxNumPendingEvents[inst.reactor]!! + numReactions += inst.reactor.allReactions.size + hasStartup = hasStartup or inst.reactor.hasStartup + return Triple(numEvents, numReactions, hasStartup) + } + + fun totalNumEventsAndReactions(main: Reactor): Pair { + val res = MutablePair(maxNumPendingEvents[main]!!, main.allReactions.size) + var hasStartup = main.hasStartup + for (inst in main.allInstantiations) { + val childRes = _totalNumEventsAndReactions(inst) + res.left += childRes.first * inst.width + res.right += childRes.second * inst.width + hasStartup = hasStartup or childRes.third + } + if (hasStartup) res.left += 1 + return res.toPair() + } + + companion object { const val libDir = "/lib/c" const val MINIMUM_CMAKE_VERSION = "3.5" } - - // Returns a possibly empty list of the federates in the current program + fun getAllFederates(): List { val res = mutableListOf() for (reactor in reactors) { @@ -48,7 +98,17 @@ class UcGenerator( return res } - private fun getAllInstantiatedReactors(top: Reactor): List { + fun getAllUcFederates(): List { + val res = mutableListOf() + for (inst in getAllFederates()) { + for (bankIdx in 0.. { val res = mutableListOf() for (inst in top.allInstantiations) { res.add(inst.reactor) @@ -56,57 +116,65 @@ class UcGenerator( } return res.distinct() } +} - fun doGenerateTopLevel(resource: Resource, context: LFGeneratorContext, srcGenPath: Path, platformGenerator: UcPlatformGenerator): GeneratorResult.Status { +class UcNonFederatedGenerator( + context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider +) : UcGenerator(context, scopeProvider) { + + + fun doGenerateReactor( + resource: Resource, + context: LFGeneratorContext, + srcGenPath: Path, + ): GeneratorResult.Status { if (!canGenerate(errorsOccurred(), mainDef, messageReporter, context)) return GeneratorResult.Status.FAILED - // generate all core files - generateFiles(mainDef, srcGenPath, getAllImportedResources(resource)) + // generate header and source files for all reactors + getAllInstantiatedReactors(mainDef.reactor).map { generateReactorFiles(it, srcGenPath) } - // generate platform specific files - platformGenerator.generatePlatformFiles() + generateReactorFiles(mainDef.reactor, srcGenPath) - if (platform.platform == PlatformType.Platform.NATIVE) { - if (platformGenerator.doCompile(context)) { - return GeneratorResult.Status.COMPILED - } else { - return GeneratorResult.Status.FAILED - } - } else { - return GeneratorResult.Status.GENERATED + for (r in getAllImportedResources(resource)) { + val generator = UcPreambleGenerator(r, fileConfig, scopeProvider) + val headerFile = fileConfig.getPreambleHeaderPath(r); + val preambleCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) + codeMaps[srcGenPath.resolve(headerFile)] = preambleCodeMap + FileUtil.writeToFile(preambleCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) } + return GeneratorResult.Status.GENERATED } override fun doGenerate(resource: Resource, context: LFGeneratorContext) { super.doGenerate(resource, context) - val federates = getAllFederates(); - // create a platform-specific generator - if (federates.isEmpty()) { - val res = doGenerateTopLevel(resource, context, fileConfig.srcGenPath, UcStandalonePlatformGenerator(this, fileConfig.srcGenPath)) - if (res == GeneratorResult.Status.FAILED) { - context.unsuccessfulFinish() - return - } - } else { - for (federate in federates) { - mainDef = federate - codeMaps.clear() - ucSources.clear() - val srcGenPath = fileConfig.srcGenPath.resolve(federate.name) - val res = doGenerateTopLevel(federate.eResource()!!, context, srcGenPath, UcFederatedPlatformGenerator(this, srcGenPath)) - - if (res == GeneratorResult.Status.FAILED) { - context.unsuccessfulFinish() - return + if (getAllFederates().isNotEmpty()) { + context.errorReporter.nowhere().error("Federated program detected in non-federated generator") + return + } + + val res = doGenerateReactor( + resource, + context, + fileConfig.srcGenPath, + ) + if (res == GeneratorResult.Status.GENERATED) { + // generate platform specific files + val platformGenerator = UcStandalonePlatformGenerator(this, fileConfig.srcGenPath) + platformGenerator.generatePlatformFiles() + + if (platform.platform == PlatformType.Platform.NATIVE) { + if (platformGenerator.doCompile(context)) { + context.finish(GeneratorResult.Status.COMPILED, codeMaps) + } else { + context.finish(GeneratorResult.Status.FAILED, codeMaps) } + } else { + context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps)) } - } - if (platform.platform == PlatformType.Platform.NATIVE) { - context.finish(GeneratorResult.Status.COMPILED, codeMaps) } else { - context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps)) + context.finish(GeneratorResult.Status.FAILED, codeMaps) } } @@ -118,13 +186,14 @@ class UcGenerator( return resources } - private fun generateReactorFiles(reactor: Reactor, srcGenPath: Path) { + fun generateReactorFiles(reactor: Reactor, srcGenPath: Path) { val generator = UcReactorGenerator(reactor, fileConfig, messageReporter) + maxNumPendingEvents.set(reactor, generator.getMaxNumPendingEvents()) + val headerFile = fileConfig.getReactorHeaderPath(reactor) val sourceFile = fileConfig.getReactorSourcePath(reactor) val reactorCodeMap = CodeMap.fromGeneratedCode(generator.generateSource()) - if (!reactor.isGeneric) - ucSources.add(sourceFile) + if (!reactor.isGeneric) ucSources.add(sourceFile) codeMaps[srcGenPath.resolve(sourceFile)] = reactorCodeMap val headerCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) codeMaps[srcGenPath.resolve(headerFile)] = headerCodeMap @@ -133,15 +202,127 @@ class UcGenerator( FileUtil.writeToFile(reactorCodeMap.generatedCode, srcGenPath.resolve(sourceFile), true) } - private fun generateFederateFiles(federate: Instantiation, srcGenPath: Path) { + override fun getTarget() = Target.UC + override fun getTargetTypes(): TargetTypes = UcTypes +} + +class UcFederatedGenerator( + context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider +) : UcGenerator(context, scopeProvider) { + + private val nonFederatedGenerator = UcNonFederatedGenerator(context, scopeProvider) + + fun totalNumEventsAndReactionsFederated(federate: UcFederate): Pair { + val eventsFromFederatedConncetions = maxNumPendingEvents[mainDef.reactor]!! + val eventsAndReactionsInFederate = nonFederatedGenerator.totalNumEventsAndReactions(federate.inst.reactor) + return Pair( + eventsFromFederatedConncetions + eventsAndReactionsInFederate.first, + eventsAndReactionsInFederate.second + ) + } + + fun doGenerateFederate( + resource: Resource, + context: LFGeneratorContext, + srcGenPath: Path, + federate: UcFederate + ): GeneratorResult.Status { + if (!canGenerate( + errorsOccurred(), + federate.inst, + messageReporter, + context + ) + ) return GeneratorResult.Status.FAILED + + // generate all core files + generateFiles(federate, srcGenPath, getAllImportedResources(resource)) + return GeneratorResult.Status.GENERATED + } + + private fun clearStateFromPreviousFederate() { + codeMaps.clear() + ucSources.clear() + maxNumPendingEvents.clear() + nonFederatedGenerator.maxNumPendingEvents.clear() + nonFederatedGenerator.codeMaps.clear() + nonFederatedGenerator.ucSources.clear() + } + + private fun createMainDef() { + for (reactor in reactors) { + if (reactor.isFederated) { + this.mainDef = LfFactory.eINSTANCE.createInstantiation() + this.mainDef.name = reactor.name + this.mainDef.reactorClass = reactor + } + } + } + + override fun doGenerate(resource: Resource, context: LFGeneratorContext) { + super.doGenerate(resource, context) + createMainDef() + for (ucFederate in getAllUcFederates()) { + clearStateFromPreviousFederate() + + val srcGenPath = if (ucFederate.isBank) { + fileConfig.srcGenPath.resolve("${ucFederate.inst.name}_${ucFederate.bankIdx}") + } else { + fileConfig.srcGenPath.resolve(ucFederate.inst.name) + } + + val platformGenerator = UcFederatedPlatformGenerator(this, srcGenPath, ucFederate) + val res = doGenerateFederate( + ucFederate.inst.eResource()!!, + context, + srcGenPath, + ucFederate + ) + + if (res == GeneratorResult.Status.FAILED) { + context.unsuccessfulFinish() + return + } else { + // generate platform specific files + platformGenerator.generatePlatformFiles() + + if (platform.platform == PlatformType.Platform.NATIVE) { + if (!platformGenerator.doCompile(context)) { + context.finish(GeneratorResult.Status.FAILED, codeMaps) + return + } + } + } + } + if (platform.platform == PlatformType.Platform.NATIVE) { + context.finish(GeneratorResult.Status.COMPILED, codeMaps) + } else { + context.finish(GeneratorResult.Status.GENERATED, codeMaps) + } + return + } + + private fun getAllImportedResources(resource: Resource): Set { + val resources: MutableSet = scopeProvider.getImportedResources(resource) + val importedRresources = resources.subtract(setOf(resource)) + resources.addAll(importedRresources.map { getAllImportedResources(it) }.flatten()) + resources.add(resource) + return resources + } + + private fun generateFederateFiles(federate: UcFederate, srcGenPath: Path) { // First thing is that we need to also generate the top-level federate reactor files - generateReactorFiles(federate.reactor, srcGenPath) + nonFederatedGenerator.generateReactorFiles(federate.inst.reactor, srcGenPath) // Then we generate a reactor which wraps around the top-level reactor in the federate. val generator = UcFederateGenerator(federate, fileConfig, messageReporter) + val top = federate.inst.eContainer() as Reactor - val headerFile = srcGenPath.resolve("Federate.h") - val sourceFile = srcGenPath.resolve("Federate.c") + // Record the number of events and reactions in this reactor + maxNumPendingEvents.set(top, generator.getMaxNumPendingEvents()) + + val headerFile = srcGenPath.resolve("lf_federate.h") + val sourceFile = srcGenPath.resolve("lf_federate.c") val federateCodeMap = CodeMap.fromGeneratedCode(generator.generateSource()) ucSources.add(sourceFile) codeMaps[srcGenPath.resolve(sourceFile)] = federateCodeMap @@ -152,16 +333,21 @@ class UcGenerator( FileUtil.writeToFile(federateCodeMap.generatedCode, srcGenPath.resolve(sourceFile), true) } - private fun generateFiles(mainDef: Instantiation, srcGenPath: Path, resources: Set) { + private fun generateFiles(federate: UcFederate, srcGenPath: Path, resources: Set) { // generate header and source files for all reactors - getAllInstantiatedReactors(mainDef.reactor).map {generateReactorFiles(it, srcGenPath)} - - if (mainDef.isAFederate) { - generateFederateFiles(mainDef, srcGenPath) - } else { - generateReactorFiles(mainDef.reactor, srcGenPath) + getAllInstantiatedReactors(federate.inst.reactor).map { + nonFederatedGenerator.generateReactorFiles( + it, + srcGenPath + ) } + generateFederateFiles(federate, srcGenPath) + + // Collect the info on the generated sources + ucSources.addAll(nonFederatedGenerator.ucSources) + codeMaps.putAll(nonFederatedGenerator.codeMaps) + for (r in resources) { val generator = UcPreambleGenerator(r, fileConfig, scopeProvider) val headerFile = fileConfig.getPreambleHeaderPath(r); @@ -174,5 +360,4 @@ class UcGenerator( override fun getTarget() = Target.UC override fun getTargetTypes(): TargetTypes = UcTypes - } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt index 7b73a834..4acbf6ff 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt @@ -3,8 +3,6 @@ package org.lflang.generator.uc import org.lflang.FileConfig import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcReactorGenerator.Companion.getEventQueueSize -import org.lflang.generator.uc.UcReactorGenerator.Companion.getReactionQueueSize import org.lflang.joinWithLn import org.lflang.lf.Reactor import org.lflang.target.property.BuildTypeProperty @@ -14,7 +12,7 @@ import java.time.LocalDateTime import kotlin.io.path.name import kotlin.math.max -class UcMakeGenerator(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig) { +class UcMakeGenerator(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig, private val numEvents: Int, private val numReactions: Int) { private val S = '$' // a little trick to escape the dollar sign with $S fun generateMake(sources: List) = with(PrependOperator) { val sources = sources.filterNot { it.name=="lf_main.c" } @@ -23,8 +21,8 @@ class UcMakeGenerator(private val main: Reactor, private val targetConfig: Targe |LFC_GEN_SOURCES = \ ${" | "..sources.joinWithLn { it.toUnixString() + if (it != sources.last()) " \\" else ""}} |LFC_GEN_MAIN = lf_main.c - |REACTION_QUEUE_SIZE = ${max(main.getReactionQueueSize(), 1)} - |EVENT_QUEUE_SIZE = ${max(main.getEventQueueSize(), 1)} + |REACTION_QUEUE_SIZE = ${max(numReactions, 1)} + |EVENT_QUEUE_SIZE = ${max(numEvents, 1)} | """.trimMargin() } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt new file mode 100644 index 00000000..51305e21 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -0,0 +1,91 @@ +package org.lflang.generator.uc + +import java.util.concurrent.atomic.AtomicInteger +import org.lflang.generator.uc.NetworkChannelType.* + +enum class NetworkChannelType { + TcpIp +} + +// Singleton object to manage port numbers +object IpPortManager { + private val currentPort = AtomicInteger(1024) // Starting port number + private val usedPorts = mutableSetOf() + + @Synchronized + fun acquirePortNumber(): Int { + + while (true) { + val port = currentPort.getAndIncrement() + if (port in 1024..65535 && usedPorts.add(port)) { + return port + } + } + } + + @Synchronized + fun reservePortNumber(port: Int) { + assert(port < 65535) + assert(!usedPorts.contains(port)) + usedPorts.add(port) + } + +} + +abstract class UcNetworkEndpoint(val iface: UcNetworkInterface) + +class UcTcpIpEndpoint(val ipAddress: String, val port: Int, iface: UcTcpIpInterface): UcNetworkEndpoint(iface) { + +} + +abstract class UcNetworkInterface(val type: NetworkChannelType) { + abstract fun connectTo(other: UcNetworkInterface): UcNetworkChannel + val endpoints = mutableListOf() +} + +class UcTcpIpInterface(ipAddress: String? = null) : UcNetworkInterface(TcpIp) { + + val ipAddress = ipAddress ?: "127.0.0.1" + + fun createEndpoint(port: Int?): UcTcpIpEndpoint { + val portNum = if (port != null) { + IpPortManager.reservePortNumber(port) + port + } else { + IpPortManager.acquirePortNumber() + } + val ep = UcTcpIpEndpoint(ipAddress, portNum, this) + endpoints.add(ep) + return ep + } + + override fun connectTo(other: UcNetworkInterface): UcNetworkChannel { + TODO("Not yet implemented") + } +} + +abstract class UcNetworkChannel( + val src: UcNetworkEndpoint, + val dest: UcNetworkEndpoint, + val serverLhs: Boolean, +) { + abstract fun generateChannelCtorSrc(): String + abstract fun generateChannelCtorDest(): String + abstract val codeType: String +} + +class UcTcpIpChannel( + val _src: UcTcpIpEndpoint, + val _dest: UcTcpIpEndpoint, + serverLhs: Boolean = true, +) : UcNetworkChannel(_src, _dest, serverLhs) { + + override fun generateChannelCtorSrc() = + "TcpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) _src.ipAddress else _dest.ipAddress}\", ${if (serverLhs) _src.port else _dest.port}, AF_INET, ${serverLhs});" + + override fun generateChannelCtorDest() = + "TcpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) _src.ipAddress else _dest.ipAddress}\", ${if (serverLhs) _src.port else _dest.port}, AF_INET, ${!serverLhs});" + + override val codeType: String + get() = "TcpIpChannel" +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt index 9d3a853c..3ed6bf1f 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt @@ -265,10 +265,9 @@ class UcReactionGenerator(private val reactor: Reactor) { if (trigger.variable.isMultiport) { generateContainedMultiportTriggerFieldInit("${instName}[i]", "&self->${trigger.container.name}[i]", trigger, trigger.variable as Port) } else { - "${instName}[i].${trigger.name} = &self->${trigger.container.name}[i].${trigger.name};" + "${instName}[i].${trigger.name} = self->${trigger.container.name}[i].${trigger.name};" } - // FIXME: This must also consider multiports and banks private fun generateContainedReactorScope(triggers: List, inst: Instantiation) = with(PrependOperator) { """| |// Generated struct providing access to ports of child reactor `${inst.name}` diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt index b9165bba..bf5bb0f5 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -77,27 +77,6 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U fun Reactor.getObservers(v: BuiltinTrigger) = allReactions.filter { it.sources.filter { it.name == v.literal }.isNotEmpty() } - fun Reactor.getEventQueueSize(): Int { - var childrenEvents = 0 - for (child in this.allInstantiations) { - childrenEvents += child.reactor.getEventQueueSize()*child.width - } - var currentReactorsEvents = 0 - for (timer in this.allTimers) { - currentReactorsEvents += 1 - } - for (action in this.allActions) { - currentReactorsEvents += action.maxNumPendingEvents - } - - if (hasShutdown) currentReactorsEvents += 1 - if (hasStartup) currentReactorsEvents += 1 - - val ucConnections = UcConnectionGenerator(this, null) - currentReactorsEvents += ucConnections.getMaxNumPendingEvents() - return childrenEvents + currentReactorsEvents - } - fun Reactor.getReactionQueueSize(): Int { var res = 0 for (child in allInstantiations) { @@ -108,6 +87,15 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U } } + fun getMaxNumPendingEvents(): Int { + var numEvents = reactor.allTimers.count() + for (action in reactor.allActions) { + numEvents += action.maxNumPendingEvents + } + numEvents += connections.getMaxNumPendingEvents() + return numEvents + } + private fun generateReactorStruct() = with(PrependOperator) { """ |typedef struct { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt index efcc1224..f8b0755c 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt @@ -2,6 +2,7 @@ package org.lflang.generator.uc import org.lflang.generator.CodeMap import org.lflang.generator.LFGeneratorContext +import org.lflang.reactor import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.type.BuildTypeType.BuildType import org.lflang.toUnixString @@ -48,8 +49,10 @@ class UcStandalonePlatformGenerator(generator: UcGenerator, val srcGenPath: Path FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) FileUtil.writeToFile(mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) - val cmakeGenerator = UcCmakeGenerator(generator.mainDef, targetConfig, generator.fileConfig) - val makeGenerator = UcMakeGenerator(mainReactor, targetConfig, generator.fileConfig) + val numEventsAndReactions = generator.totalNumEventsAndReactions(generator.mainDef.reactor) + + val cmakeGenerator = UcCmakeGenerator(generator.mainDef, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) + val makeGenerator = UcMakeGenerator(mainReactor, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) FileUtil.writeToFile(cmakeGenerator.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); try { diff --git a/src/federated.c b/src/federated.c index e9199a8f..2376ab70 100644 --- a/src/federated.c +++ b/src/federated.c @@ -7,6 +7,7 @@ void LogicalConnection_trigger_downstreams(Connection *self, const void *value, size_t value_size); void FederatedConnectionBundle_connect_to_peers(FederatedConnectionBundle **bundles, size_t bundles_size) { + LF_DEBUG(FED, "Connecting to %zu federated peers", bundles_size); lf_ret_t ret; Environment *env = bundles[0]->parent->env; @@ -35,6 +36,8 @@ void FederatedConnectionBundle_connect_to_peers(FederatedConnectionBundle **bund env->platform->wait_for(env->platform, wait_before_retry); } } + + LF_DEBUG(FED, "Established connection to all %zu federated peers", bundles_size); } // Called when a reaction does lf_set(outputPort). Should buffer the output data @@ -150,7 +153,7 @@ void FederatedInputConnection_ctor(FederatedInputConnection *self, Reactor *pare self->delay = delay; self->type = type; self->last_known_tag = NEVER_TAG; - self->safe_to_assume_absent = FOREVER; + self->safe_to_assume_absent = 0; // FIXME: This should be set by the user } void FederatedConnectionBundle_handle_start_tag_signal(FederatedConnectionBundle *self, const FederateMessage *_msg) { @@ -228,7 +231,10 @@ void FederatedConnectionBundle_handle_tagged_msg(FederatedConnectionBundle *self LF_WARN(FED, "Tried scheduling event after stop tag. Dropping\n"); break; case LF_PAST_TAG: - LF_WARN(FED, "Tried scheduling event to a past tag. Dropping\n"); + LF_ERR(FED, "Safe-to-process violation! Tried scheduling event to a past tag. Handling now instead!\n"); + event.tag = sched->current_tag(sched); + event.tag.microstep++; + sched->schedule_at_locked(sched, &event); break; case LF_OK: env->platform->new_async_event(env->platform); diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index b5212f2d..da04b297 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -112,6 +112,7 @@ static lf_ret_t _TcpIpChannel_server_bind(TcpIpChannel *self) { struct sockaddr_in serv_addr; serv_addr.sin_family = self->protocol_family; serv_addr.sin_port = htons(self->port); + TCP_IP_CHANNEL_INFO("Bind to %s:%u", self->host, self->port); // turn human-readable address into something the os can work with if (inet_pton(self->protocol_family, self->host, &serv_addr.sin_addr) <= 0) { @@ -341,7 +342,11 @@ static lf_ret_t _TcpIpChannel_receive(NetworkChannel *untyped_self, FederateMess static void TcpIpChannel_close_connection(NetworkChannel *untyped_self) { TcpIpChannel *self = (TcpIpChannel *)untyped_self; - TCP_IP_CHANNEL_DEBUG("Close connection"); + TCP_IP_CHANNEL_DEBUG("Closing connection"); + + if (self->state != NETWORK_CHANNEL_STATE_CONNECTED) { + return; + } if (self->is_server && self->client != 0) { if (close(self->client) < 0) { diff --git a/src/queues.c b/src/queues.c index b835ce7f..7ebe6e3b 100644 --- a/src/queues.c +++ b/src/queues.c @@ -19,7 +19,7 @@ lf_ret_t EventQueue_insert(EventQueue *self, Event *event) { LF_DEBUG(QUEUE, "Inserting event with tag %" PRId64 ":%" PRIu32 " into EventQueue", event->tag.time, event->tag.microstep); if (self->size >= EVENT_QUEUE_SIZE) { - LF_ERR(QUEUE, "EventQueue is full"); + LF_ERR(QUEUE, "EventQueue is full has size %d", self->size); assert(false); return LF_OUT_OF_BOUNDS; } diff --git a/src/reaction.c b/src/reaction.c index d8a253d5..c27536c0 100644 --- a/src/reaction.c +++ b/src/reaction.c @@ -10,14 +10,14 @@ size_t Reaction_get_level(Reaction *self) { return self->level; } -static size_t calculate_port_level(Port *port) { - size_t current = 0; +int calculate_port_level(Port *port) { + int current = -1; if (port->conn_in) { Port *final_upstream_port = port->conn_in->get_final_upstream(port->conn_in); if (final_upstream_port) { for (size_t k = 0; k < final_upstream_port->sources.size; k++) { Reaction *upstream = final_upstream_port->sources.reactions[k]; - size_t upstream_level = upstream->get_level(upstream); + int upstream_level = upstream->get_level(upstream); if (upstream_level > current) { current = upstream_level; } @@ -27,7 +27,7 @@ static size_t calculate_port_level(Port *port) { for (size_t i = 0; i < port->sources.size; i++) { Reaction *source = port->sources.reactions[i]; - size_t source_level = source->get_level(source); + int source_level = source->get_level(source); if (source_level > current) { current = source_level; } @@ -38,12 +38,12 @@ static size_t calculate_port_level(Port *port) { } size_t Reaction_calculate_trigger_level(Reaction *self, Trigger *trigger) { - size_t max_level = 0; + int max_level = 0; if (trigger->type == TRIG_INPUT || trigger->type == TRIG_OUTPUT) { Port *port = (Port *)trigger; for (size_t j = 0; j < port->effects.size; j++) { if (port->effects.reactions[j] == self) { - size_t level_from_port = calculate_port_level(port) + 1; + int level_from_port = calculate_port_level(port) + 1; if (level_from_port > max_level) { max_level = level_from_port; } @@ -51,7 +51,7 @@ size_t Reaction_calculate_trigger_level(Reaction *self, Trigger *trigger) { } for (size_t j = 0; j < port->observers.size; j++) { if (port->observers.reactions[j] == self) { - size_t level_from_port = calculate_port_level(port) + 1; + int level_from_port = calculate_port_level(port) + 1; if (level_from_port > max_level) { max_level = level_from_port; } diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index 6dd19453..423e6b2b 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -62,7 +62,6 @@ static lf_ret_t Scheduler_federated_acquire_tag(Scheduler *untyped_self, tag_t n FederatedConnectionBundle *bundle = env->net_bundles[i]; for (size_t j = 0; j < bundle->inputs_size; j++) { FederatedInputConnection *input = bundle->inputs[j]; - validate(input->safe_to_assume_absent == FOREVER); // TODO: We only support dataflow like things now // Find the max safe-to-assume-absent value and go to sleep waiting for this. if (lf_tag_compare(input->last_known_tag, next_tag) < 0) { LF_DEBUG(SCHED, "Input %p is unresolved, latest known tag was %" PRId64 ":%" PRIu32, input, diff --git a/test/lf/src/FederatedBank.lf b/test/lf/src/FederatedBank.lf new file mode 100644 index 00000000..9e190b1d --- /dev/null +++ b/test/lf/src/FederatedBank.lf @@ -0,0 +1,40 @@ +target uC { + platform: Native, + timeout: 1 msec +} + + +reactor Src { + output out: int + + reaction(startup) -> out {= + printf("Hello from Src!\n"); + lf_set(out, 42); + =} +} + +reactor Fed(bank_idx: int = 0) { + output out: int + input in: int + input in2: int + + reaction(in) -> out {= + printf("Received %d from src \n", in->value); + lf_set(out, self->bank_idx); + =} + + reaction(in2) {= + printf("Received %d from myself\n", in2->value); + =} +} + + +federated reactor { + src = new Src() + dests = new [2] Fed() + + (src.out)+ -> dests.in + + dests.out -> dests.in2 + +} \ No newline at end of file diff --git a/test/lf/src/FederatedConnection.lf b/test/lf/src/FederatedConnection.lf index 704d8924..d56ebc29 100644 --- a/test/lf/src/FederatedConnection.lf +++ b/test/lf/src/FederatedConnection.lf @@ -1,5 +1,6 @@ target uC { - platform: Native + platform: Native, + timeout: 1 msec } reactor Src(id: int = 0) { diff --git a/test/lf/src/FederatedLoopbackConnection.lf b/test/lf/src/FederatedLoopbackConnection.lf new file mode 100644 index 00000000..e47dea5f --- /dev/null +++ b/test/lf/src/FederatedLoopbackConnection.lf @@ -0,0 +1,33 @@ +target uC { + platform: Native, + timeout: 1 msec +} + +reactor Src(id: int = 0) { + output out: int + input in: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} + reaction(in) {= + printf("Received %d from myself\n", in->value); + =} +} + +reactor Dst { + input in: int + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + =} +} + +federated reactor { + r1 = new Src(id=42) + r2 = new Dst() + r1.out -> r2.in + r1.out -> r1.in +} \ No newline at end of file From 36898833484b89ea7220362f2c2627b904d636f7 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 8 Jan 2025 13:06:23 +0100 Subject: [PATCH 19/65] Formatting --- src/platform/posix/tcp_ip_channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index da04b297..785553ee 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -112,7 +112,7 @@ static lf_ret_t _TcpIpChannel_server_bind(TcpIpChannel *self) { struct sockaddr_in serv_addr; serv_addr.sin_family = self->protocol_family; serv_addr.sin_port = htons(self->port); - TCP_IP_CHANNEL_INFO("Bind to %s:%u", self->host, self->port); + TCP_IP_CHANNEL_INFO("Bind to %s:%u", self->host, self->port); // turn human-readable address into something the os can work with if (inet_pton(self->protocol_family, self->host, &serv_addr.sin_addr) <= 0) { From 9eb722de2b0fd00bcfebeeea0b73539e72986e4f Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 9 Jan 2025 15:12:08 +0100 Subject: [PATCH 20/65] More WIP --- .gitignore | 1 + examples/riot/hello/main.c | 2 +- include/reactor-uc/environment.h | 1 + include/reactor-uc/logging.h | 31 ++--- include/reactor-uc/macros.h | 4 +- .../platform/posix/tcp_ip_channel.h | 2 + include/reactor-uc/tag.h | 1 + .../main/java/org/lflang/target/Target.java | 1 + .../generator/uc/UcCmakeFederatedGenerator.kt | 2 + .../lflang/generator/uc/UcCmakeGenerator.kt | 2 + .../generator/uc/UcConnectionGenerator.kt | 125 ++++++++++-------- .../generator/uc/UcFederateGenerator.kt | 3 +- .../uc/UcFederatedLaunchScriptGenerator.kt | 6 +- .../generator/uc/UcFederatedMainGenerator.kt | 3 +- .../org/lflang/generator/uc/UcFileConfig.kt | 8 -- .../generator/uc/UcInstanceGenerator.kt | 16 ++- .../lflang/generator/uc/UcMainGenerator.kt | 1 + .../generator/uc/UcParameterGenerator.kt | 8 +- .../lflang/generator/uc/UcPortGenerator.kt | 7 +- .../generator/uc/UcReactionGenerator.kt | 17 +-- .../lflang/generator/uc/UcReactorGenerator.kt | 12 +- src/environment.c | 6 +- src/federated.c | 19 ++- src/logging.c | 7 +- src/platform/posix/posix.c | 6 +- src/platform/posix/tcp_ip_channel.c | 72 ++++++++-- src/platform/riot/coap_udp_ip_channel.c | 4 +- test/lf/src/FederatedBank.lf | 7 + test/lf/src/FederatedBankMultiport.lf | 34 +++++ 29 files changed, 270 insertions(+), 138 deletions(-) create mode 100644 test/lf/src/FederatedBankMultiport.lf diff --git a/.gitignore b/.gitignore index 696ea802..b4b9422c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ **/build/ **/src-gen/ **/bin/ +**/*.log benchmarks/include .vscode/ cmake-build-debug diff --git a/examples/riot/hello/main.c b/examples/riot/hello/main.c index f6900958..c752cafe 100755 --- a/examples/riot/hello/main.c +++ b/examples/riot/hello/main.c @@ -5,7 +5,7 @@ LF_DEFINE_REACTION_BODY(TimerSource, r) { LF_SCOPE_SELF(TimerSource); LF_SCOPE_ENV(); - printf("TimerSource World @ %lld\n", env->get_elapsed_logical_time(env)); + printf("Hello World @ %lld\n", env->get_elapsed_logical_time(env)); } int main() { diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index 32d698eb..6b346703 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -9,6 +9,7 @@ #include "reactor-uc/scheduler.h" typedef struct Environment Environment; +extern Environment *_lf_environment; struct Environment { Reactor *main; // The top-level reactor of the program. diff --git a/include/reactor-uc/logging.h b/include/reactor-uc/logging.h index 9ea8d7ba..ef41bf23 100644 --- a/include/reactor-uc/logging.h +++ b/include/reactor-uc/logging.h @@ -5,10 +5,11 @@ // The different verbosity levels supported #define LF_LOG_LEVEL_OFF 0 -#define LF_LOG_LEVEL_INFO 1 -#define LF_LOG_LEVEL_ERR 2 -#define LF_LOG_LEVEL_WARN 3 -#define LF_LOG_LEVEL_DEBUG 4 +#define LF_LOG_LEVEL_ERROR 1 +#define LF_LOG_LEVEL_WARN 2 +#define LF_LOG_LEVEL_INFO 3 +#define LF_LOG_LEVEL_LOG 4 +#define LF_LOG_LEVEL_DEBUG 5 // Add color codes to the output #define LF_COLORIZE_LOGS 1 @@ -18,17 +19,17 @@ #ifndef NDEBUG #define LF_LOG_LEVEL_ALL LF_LOG_LEVEL_DEBUG #else -#define LF_LOG_LEVEL_ALL LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_ALL LF_LOG_LEVEL_ERROR #endif #endif // Define the log level for each module. If not defined, use LF_LOG_LEVEL_ALL -// or set to LF_LOG_LEVEL_ERR if LF_LOG_LEVEL_ALL is not defined. +// or set to LF_LOG_LEVEL_ERROR if LF_LOG_LEVEL_ALL is not defined. #ifndef LF_LOG_LEVEL_ENV #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_ENV LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_ENV LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_ENV LF_LOG_LEVEL_ERROR #endif #endif @@ -36,7 +37,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_SCHED LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_SCHED LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_SCHED LF_LOG_LEVEL_ERROR #endif #endif @@ -44,7 +45,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_QUEUE LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_QUEUE LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_QUEUE LF_LOG_LEVEL_ERROR #endif #endif @@ -52,7 +53,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_FED LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_FED LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_FED LF_LOG_LEVEL_ERROR #endif #endif @@ -60,7 +61,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_TRIG LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_TRIG LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_TRIG LF_LOG_LEVEL_ERROR #endif #endif @@ -68,7 +69,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_PLATFORM LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_PLATFORM LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_PLATFORM LF_LOG_LEVEL_ERROR #endif #endif @@ -76,7 +77,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_CONN LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_CONN LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_CONN LF_LOG_LEVEL_ERROR #endif #endif @@ -84,7 +85,7 @@ #ifdef LF_LOG_LEVEL_ALL #define LF_LOG_LEVEL_NET LF_LOG_LEVEL_ALL #else -#define LF_LOG_LEVEL_NET LF_LOG_LEVEL_ERR +#define LF_LOG_LEVEL_NET LF_LOG_LEVEL_ERROR #endif #endif @@ -106,7 +107,7 @@ } \ } while (0) -#define LF_ERR(module, fmt, ...) LF_LOG(LF_LOG_LEVEL_ERR, module, fmt, ##__VA_ARGS__) +#define LF_ERR(module, fmt, ...) LF_LOG(LF_LOG_LEVEL_ERROR, module, fmt, ##__VA_ARGS__) #define LF_WARN(module, fmt, ...) LF_LOG(LF_LOG_LEVEL_WARN, module, fmt, ##__VA_ARGS__) #define LF_INFO(module, fmt, ...) LF_LOG(LF_LOG_LEVEL_INFO, module, fmt, ##__VA_ARGS__) #define LF_DEBUG(module, fmt, ...) LF_LOG(LF_LOG_LEVEL_DEBUG, module, fmt, ##__VA_ARGS__) diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index 98749ea0..73282a67 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -597,8 +597,8 @@ typedef struct FederatedOutputConnection FederatedOutputConnection; #define LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(ReactorName, OutputName, SerializeFunc) \ ReactorName##_##OutputName##_conn_ctor(&self->OutputName, self->super.parent, &self->super); \ - self->outputs[_inputs_idx] = &self->OutputName.super; \ - self->serialize_hooks[_inputs_idx] = SerializeFunc; \ + self->outputs[_outputs_idx] = &self->OutputName.super; \ + self->serialize_hooks[_outputs_idx] = SerializeFunc; \ _outputs_idx++; typedef struct FederatedInputConnection FederatedInputConnection; diff --git a/include/reactor-uc/platform/posix/tcp_ip_channel.h b/include/reactor-uc/platform/posix/tcp_ip_channel.h index 4a684cf0..43a58bc2 100644 --- a/include/reactor-uc/platform/posix/tcp_ip_channel.h +++ b/include/reactor-uc/platform/posix/tcp_ip_channel.h @@ -26,7 +26,9 @@ struct TcpIpChannel { int fd; int client; int send_failed_event_fds; // These file descriptors are used to signal the recv select to stop blocking + int terminate_event_fds; NetworkChannelState state; + pthread_mutex_t mutex; const char *host; unsigned short port; diff --git a/include/reactor-uc/tag.h b/include/reactor-uc/tag.h index 443ba5e1..da5f2065 100644 --- a/include/reactor-uc/tag.h +++ b/include/reactor-uc/tag.h @@ -21,6 +21,7 @@ #define SECS(t) ((interval_t)(t * 1000000000LL)) #define SECOND(t) ((interval_t)(t * 1000000000LL)) #define SECONDS(t) ((interval_t)(t * 1000000000LL)) +#define MIN(t) ((interval_t)(t * 60000000000LL)) #define MINUTE(t) ((interval_t)(t * 60000000000LL)) #define MINUTES(t) ((interval_t)(t * 60000000000LL)) #define HOUR(t) ((interval_t)(t * 3600000000000LL)) diff --git a/lfc/core/src/main/java/org/lflang/target/Target.java b/lfc/core/src/main/java/org/lflang/target/Target.java index 7f504cfd..c1872607 100644 --- a/lfc/core/src/main/java/org/lflang/target/Target.java +++ b/lfc/core/src/main/java/org/lflang/target/Target.java @@ -573,6 +573,7 @@ public void initialize(TargetConfig config) { TimeOutProperty.INSTANCE, FastProperty.INSTANCE, KeepaliveProperty.INSTANCE, + LoggingProperty.INSTANCE, CmakeIncludeProperty.INSTANCE, FilesProperty.INSTANCE); diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt index 1cb750f6..5eab2180 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt @@ -9,6 +9,7 @@ import org.lflang.lf.Instantiation import org.lflang.lf.Reactor import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.CmakeIncludeProperty +import org.lflang.target.property.LoggingProperty import org.lflang.target.property.PlatformProperty import org.lflang.target.property.type.PlatformType import org.lflang.util.FileUtil @@ -67,6 +68,7 @@ class UcCmakeFederatedGenerator(private val federate: UcFederate, private val ta | RUNTIME DESTINATION $S{CMAKE_INSTALL_BINDIR} | OPTIONAL |) + |add_compile_definitions("LF_LOG_LEVEL_ALL=LF_LOG_LEVEL_${targetConfig.getOrDefault(LoggingProperty.INSTANCE).name.uppercase()}") | |add_subdirectory(reactor-uc) |target_link_libraries($S{LF_MAIN_TARGET} PRIVATE reactor-uc) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index 9c3c4977..f25a5daf 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -9,6 +9,7 @@ import org.lflang.lf.Instantiation import org.lflang.lf.Reactor import org.lflang.target.property.BuildTypeProperty import org.lflang.target.property.CmakeIncludeProperty +import org.lflang.target.property.LoggingProperty import org.lflang.target.property.PlatformProperty import org.lflang.target.property.type.PlatformType import org.lflang.util.FileUtil @@ -66,6 +67,7 @@ class UcCmakeGenerator(private val mainDef: Instantiation, private val targetCon | RUNTIME DESTINATION $S{CMAKE_INSTALL_BINDIR} | OPTIONAL |) + |add_compile_definitions(LF_LOG_LEVEL_ALL=${targetConfig.getOrDefault(LoggingProperty.INSTANCE).ordinal}) | |add_subdirectory(reactor-uc $S{CMAKE_CURRENT_LIST_DIR}) |target_link_libraries($S{LF_MAIN_TARGET} PRIVATE reactor-uc) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 3d126920..3f45da7e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -5,6 +5,7 @@ import org.lflang.generator.PrependOperator import org.lflang.generator.orNever import org.lflang.generator.uc.UcConnectionGenerator.Companion.networkChannelType import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.width @@ -22,7 +23,7 @@ class UcFederate(val inst: Instantiation, val bankIdx: Int) { } fun getInterface(ifaceType: NetworkChannelType): UcNetworkInterface = - interfaces.find{ it.type == ifaceType }!! + interfaces.find { it.type == ifaceType }!! override fun equals(other: Any?): Boolean { if (this === other) return true @@ -35,7 +36,8 @@ class UcFederate(val inst: Instantiation, val bankIdx: Int) { } class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { - val isFederated = (src.varRef.container != null) && (src.varRef.container.isAFederate) && (dest.varRef.container != null) && dest.varRef.container.isAFederate && (src.varRef.container != dest.varRef.container) + val isFederated = (src.federate != null) && (dest.federate != null) && (src.federate != dest.federate) + companion object { fun parseConnectionChannels(conn: Connection): List { val res = mutableListOf() @@ -87,7 +89,6 @@ open class UcGroupedConnection( val src: VarRef, val channels: List, val lfConn: Connection, - uid: Int ) { val delay = lfConn.delay.orNever().toCCode() val isPhysical = lfConn.isPhysical @@ -96,14 +97,20 @@ open class UcGroupedConnection( val srcPort = src.variable as Port val isDelayed = lfConn.isPhysical || !isLogical // We define physical connections as delayed. - val bankWidth = srcInst?.width ?: 1 + private var uid: Int = -1 + + val bankWidth = if (srcInst != null) srcInst.codeWidth else 1 val portWidth = srcPort.width - val uniqueName = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" val numDownstreams = { - val frequencyMap = channels.groupingBy { Pair(it.src.port_idx, it.src.bank_idx) }.eachCount() + val frequencyMap = channels.groupingBy { Pair(it.src.getCodePortIdx(), it.src.getCodeBankIdx()) }.eachCount() frequencyMap.values.maxOrNull() ?: 0 } val maxNumPendingEvents = 16 // FIXME: Must be derived from the program + + fun assignUid(id: Int) { + uid = id + } + fun getUniqueName()= "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" } class UcFederatedGroupedConnection( @@ -113,7 +120,7 @@ class UcFederatedGroupedConnection( uid: Int, val srcFed: UcFederate, val destFed: UcFederate, -) : UcGroupedConnection(src, channels, lfConn, uid) { +) : UcGroupedConnection(src, channels, lfConn) { val serializeFunc = "serialize_payload_default" val deserializeFunc = "deserialize_payload_default" @@ -156,10 +163,15 @@ class UcFederatedConnectionBundle( // Convenience class around a port variable reference. It is used to encapsulate the management of multi-connections // where a single lhs port has to class UcChannel(val varRef: VarRef, val port_idx: Int, val bank_idx: Int) { + val federate = + if (varRef.container != null && varRef.container.isAFederate) UcFederate(varRef.container, bank_idx) else null + + fun getCodePortIdx() = port_idx + fun getCodeBankIdx() = if (federate == null) bank_idx else 0 + private val portOfContainedReactor = varRef.container != null - private val index = "${if (portOfContainedReactor) "[${bank_idx}]" else ""}[${port_idx}]" - private val reactorInstance = if (portOfContainedReactor) "${varRef.container.name}[${bank_idx}]." else "" - private val portInstance = "${varRef.name}[${port_idx}]" + private val reactorInstance = if (portOfContainedReactor) "${varRef.container.name}[${getCodeBankIdx()}]." else "" + private val portInstance = "${varRef.name}[${getCodePortIdx()}]" fun generateChannelPointer() = "&self->${reactorInstance}${portInstance}" } @@ -225,23 +237,21 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: channels.filter { it.conn.delayString == c.conn.delayString && it.conn.isPhysical == c.conn.isPhysical && - it.dest.varRef == c.dest.varRef && - it.src.varRef == c.src.varRef && - it.src.bank_idx == c.src.bank_idx && - it.dest.bank_idx == c.dest.bank_idx && + it.src.federate == c.src.federate && + it.dest.federate == c.dest.federate && it.conn.networkChannelType == c.conn.networkChannelType } - val srcFed = allFederates.find {it == UcFederate(c.src.varRef.container, c.src.bank_idx)}!! - val destFed = allFederates.find {it == UcFederate(c.dest.varRef.container, c.dest.bank_idx)}!! + val srcFed = allFederates.find { it == UcFederate(c.src.varRef.container, c.src.bank_idx) }!! + val destFed = allFederates.find { it == UcFederate(c.dest.varRef.container, c.dest.bank_idx) }!! val groupedConnection = UcFederatedGroupedConnection( - c.src.varRef, - grouped, - c.conn, - res.size, - srcFed, - destFed, - ) + c.src.varRef, + grouped, + c.conn, + res.size, + srcFed, + destFed, + ) res.add(groupedConnection) channels.removeAll(grouped) @@ -251,16 +261,19 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: channels.filter { it.conn.delayString == c.conn.delayString && it.conn.isPhysical == c.conn.isPhysical && - it.src.varRef == c.src.varRef + !it.isFederated && + it.src.varRef == c.src.varRef && + it.src.federate == c.src.federate } - val groupedConnection = UcGroupedConnection(c.src.varRef, grouped, c.conn, res.size) + val groupedConnection = UcGroupedConnection(c.src.varRef, grouped, c.conn) res.add(groupedConnection) channels.removeAll(grouped) } } return res } + companion object { private val Connection.delayString get(): String = this.delay.orNever().toCCode() @@ -294,8 +307,8 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: when (g) { is UcFederatedGroupedConnection -> { val group = groupedSet.filterIsInstance().filter { - it.srcFed == g.srcFed && - it.destFed == g.destFed + (it.srcFed == g.srcFed && it.destFed == g.destFed) || + (it.srcFed == g.destFed && it.destFed == g.srcFed) } bundles.add( @@ -340,12 +353,18 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: // FIXME: How can we handle banks here? nonFederatedConnections.addAll( grouped - .filterNot{ it is UcFederatedGroupedConnection} - .filter{it.srcInst == federate!!.inst} + .filterNot { it is UcFederatedGroupedConnection } + .filter { it.channels.fold(true, { acc, c -> acc && (c.src.federate == federate) }) } ) } else { nonFederatedConnections.addAll(grouped) } + + // Assign a unique ID to each connection to avoid possible naming conflicts. + val allGroupedConnections = federatedConnectionBundles.map {it.groupedConnections}.flatten().plus(nonFederatedConnections) + allGroupedConnections.forEachIndexed { idx, el -> + el.assignUid(idx) + } } @@ -362,11 +381,9 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: // Find all outgoing federated grouped connections from this port. for (federatedConnectionBundle in federatedConnectionBundles) { - if (federatedConnectionBundle.src == federate) { - for (groupedConn in federatedConnectionBundle.groupedConnections) { - if (groupedConn.srcInst == instantiation && groupedConn.srcPort == port) { - count += 1 - } + for (groupedConn in federatedConnectionBundle.groupedConnections) { + if (groupedConn.srcFed == federate && groupedConn.srcInst == instantiation && groupedConn.srcPort == port) { + count += 1 } } } @@ -374,28 +391,28 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: } private fun generateLogicalSelfStruct(conn: UcGroupedConnection) = - "LF_DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()});" + "LF_DEFINE_LOGICAL_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()});" private fun generateLogicalCtor(conn: UcGroupedConnection) = - "LF_DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()});" + "LF_DEFINE_LOGICAL_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()});" private fun generateDelayedSelfStruct(conn: UcGroupedConnection) = - "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay});" + "LF_DEFINE_DELAYED_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay});" private fun generateDelayedCtor(conn: UcGroupedConnection) = - "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" + "LF_DEFINE_DELAYED_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.numDownstreams()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" private fun generateFederatedInputSelfStruct(conn: UcGroupedConnection) = - "LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" + "LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents});" private fun generateFederatedInputCtor(conn: UcGroupedConnection) = - "LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" + "LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()}, ${conn.maxNumPendingEvents}, ${conn.delay}, ${conn.isPhysical});" private fun generateFederatedOutputSelfStruct(conn: UcGroupedConnection) = - "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()});" + "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()});" private fun generateFederatedOutputCtor(conn: UcGroupedConnection) = - "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.uniqueName}, ${conn.srcPort.type.toText()});" + "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()});" private fun generateFederatedConnectionSelfStruct(conn: UcFederatedGroupedConnection) = if (conn.srcFed == federate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct(conn) @@ -404,19 +421,19 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: if (conn.srcFed == federate) generateFederatedOutputCtor(conn) else generateFederatedInputCtor(conn) private fun generateFederatedOutputInstance(conn: UcGroupedConnection) = - "LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.uniqueName});" + "LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" private fun generateFederatedInputInstance(conn: UcGroupedConnection) = - "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.uniqueName});" + "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" private fun generateFederatedConnectionInstance(conn: UcFederatedGroupedConnection) = if (conn.srcFed == federate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance(conn) private fun generateInitializeFederatedOutput(conn: UcFederatedGroupedConnection) = - "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.uniqueName}, ${conn.serializeFunc});" + "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.serializeFunc});" private fun generateInitializeFederatedInput(conn: UcFederatedGroupedConnection) = - "LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(${reactor.codeType}, ${conn.uniqueName}, ${conn.deserializeFunc});" + "LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.deserializeFunc});" private fun generateInitializeFederatedConnection(conn: UcFederatedGroupedConnection) = if (conn.srcFed == federate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput(conn) @@ -424,7 +441,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: private fun generateReactorCtorCode(conn: UcGroupedConnection) = with(PrependOperator) { """ - |${if (conn.isLogical) "LF_INITIALIZE_LOGICAL_CONNECTION(" else "LF_INITIALIZE_DELAYED_CONNECTION("}${reactor.codeType}, ${conn.uniqueName}, ${conn.bankWidth}, ${conn.portWidth}); + |${if (conn.isLogical) "LF_INITIALIZE_LOGICAL_CONNECTION(" else "LF_INITIALIZE_DELAYED_CONNECTION("}${reactor.codeType}, ${conn.getUniqueName()}, ${conn.bankWidth}, ${conn.portWidth}); """.trimMargin() }; @@ -433,7 +450,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: private fun generateConnectChannel(groupedConn: UcGroupedConnection, channel: UcConnectionChannel) = with(PrependOperator) { - """|lf_connect((Connection *) &self->${groupedConn.uniqueName}[${channel.src.bank_idx}][${channel.src.port_idx}], (Port *) ${channel.src.generateChannelPointer()}, (Port *) ${channel.dest.generateChannelPointer()}); + """|lf_connect((Connection *) &self->${groupedConn.getUniqueName()}[${channel.src.getCodeBankIdx()}][${channel.src.getCodePortIdx()}], (Port *) ${channel.src.generateChannelPointer()}, (Port *) ${channel.dest.generateChannelPointer()}); """.trimMargin() } @@ -447,18 +464,18 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: conn: UcFederatedGroupedConnection ) = conn.channels.joinWithLn { - "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.uniqueName}, (Port*) &self->${bundle.src.inst.name}[0].${it.src.varRef.name}[0]);" + "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${bundle.src.inst.name}[0].${it.src.varRef.name}[${it.src.port_idx}]);" } private fun generateConnectFederateInputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection) = conn.channels.joinWithLn { - "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.uniqueName}, (Port*) &self->${bundle.dest.inst.name}[0].${it.dest.varRef.name}[0]);" + "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${bundle.dest.inst.name}[0].${it.dest.varRef.name}[${it.dest.port_idx}]);" } private fun generateFederateConnectionStatements(conn: UcFederatedConnectionBundle) = conn.groupedConnections.joinWithLn { - if (conn.src == federate) { + if (it.srcFed == federate) { generateConnectFederateOutputChannel(conn, it) } else { generateConnectFederateInputChannel(conn, it) @@ -511,7 +528,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: ${" | "..bundle.groupedConnections.joinWithLn { generateFederatedConnectionInstance(it) }} | LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(${bundle.numInputs(federate!!)}, ${ bundle.numOutputs( - federate !! + federate!! ) }); |} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(${bundle.src.codeType}, ${bundle.dest.codeType}); @@ -566,8 +583,8 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: fun generateReactorStructFields() = nonFederatedConnections.joinToString(prefix = "// Connections \n", separator = "\n", postfix = "\n") { - if (it.isLogical) "LF_LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.uniqueName}, ${it.bankWidth}, ${it.portWidth});" - else "LF_DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.uniqueName}, ${it.bankWidth}, ${it.portWidth});" + if (it.isLogical) "LF_LOGICAL_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" + else "LF_DELAYED_CONNECTION_INSTANCE(${reactor.codeType}, ${it.getUniqueName()}, ${it.bankWidth}, ${it.portWidth});" } fun getMaxNumPendingEvents(): Int { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 6473ab6c..9f00f6c5 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -10,14 +10,13 @@ class UcFederateGenerator(private val federate: UcFederate, private val fileConf private val container = federate.inst.eContainer() as Reactor private val reactor = federate.inst.reactor private val connections = UcConnectionGenerator(container, federate) - private val parameters = UcParameterGenerator(container) + private val parameters = UcParameterGenerator(container, federate) private val ports = UcPortGenerator(container, connections) private val reactions = UcReactionGenerator(container) private val instances = UcInstanceGenerator(container, parameters, ports, connections, reactions, fileConfig, messageReporter) private val headerFile = "lf_federate.h" fun numBundles() = connections.getNumFederatedConnectionBundles() - // FIXME: Calculate event queue size for federate... private val includeGuard = "LFC_GEN_FEDERATE_${federate.inst.name.uppercase()}_H" diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt index 95a130f1..4b55df34 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt @@ -42,7 +42,11 @@ class UcFederatedLaunchScriptGenerator(private val fileConfig: UcFileConfig) { fun launchFederate(federate: UcFederate) = with(PrependOperator) { """ |echo "#### Launching federate ${federate.codeType}" - |${fileConfig.binPath}/${federate.codeType} & + |if [ "${S}1" = "-l" ]; then + | ${fileConfig.binPath}/${federate.codeType} | tee ${federate.codeType}.log & + |else + | ${fileConfig.binPath}/${federate.codeType} & + |fi |pids+=($S!) | """.trimMargin() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt index 624101f1..b5f5a5f7 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt @@ -29,6 +29,7 @@ class UcFederatedMainGenerator( |#include "lf_federate.h" |static ${main.codeType} main_reactor; |static Environment lf_environment; + |Environment *_lf_environment = &lf_environment; |void lf_exit(void) { | Environment_free(&lf_environment); |} @@ -36,7 +37,7 @@ class UcFederatedMainGenerator( | Environment_ctor(&lf_environment, (Reactor *)&main_reactor); | lf_environment.scheduler->duration = ${getDuration()}; | lf_environment.scheduler->keep_alive = ${keepAlive()}; - | lf_environment.scheduler->leader = ${top.instantiations.first() == main.inst}; + | lf_environment.scheduler->leader = ${top.instantiations.first() == main.inst && main.bankIdx == 0}; | lf_environment.fast_mode = ${fast()}; | lf_environment.has_async_events = ${main.inst.reactor.inputs.isNotEmpty()}; | ${main.codeType}_ctor(&main_reactor, NULL, &lf_environment); diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt index ba01f19e..09e1347c 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt @@ -26,7 +26,6 @@ class UcFileConfig(resource: Resource, srcGenBasePath: Path, useHierarchicalBin: /** Relative path to the directory where all source files for this resource should be generated in. */ private fun getGenDir(r: Resource): Path = this.getDirectory(r).resolve(r.name) - /** Path to the header file with preambles defined for this reactor */ fun getPreambleHeaderPath(r: Resource): Path = getGenDir(r).resolve("_lf_preamble.h") @@ -36,13 +35,6 @@ class UcFileConfig(resource: Resource, srcGenBasePath: Path, useHierarchicalBin: /** Path to the header file corresponding to this reactor */ fun getReactorHeaderPath(r: Reactor): Path = getGenDir(r.eResource()).resolve("${r.name}.h") - /** Path to the source file corresponding to this reactor (needed for non generic reactors) */ - fun getFederateSourcePath(f: Instantiation): Path = getGenDir(f.eResource()).resolve("${f.codeTypeFederate}.c") - - /** Path to the header file corresponding to this reactor */ - fun getFederateHeaderPath(f: Instantiation): Path = getGenDir(f.eResource()).resolve("${f.codeTypeFederate}.h") - - /** Path to the build directory containing CMake-generated files */ val buildPath: Path get() = this.outPath.resolve("build") } \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt index aefc91a0..0aeec0b6 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcInstanceGenerator.kt @@ -19,6 +19,8 @@ class UcInstanceGenerator( companion object { val Instantiation.width get(): Int = widthSpec?.getWidth()?:1 + val Instantiation.codeWidth + get(): Int = if (this.isAFederate) 1 else width val Instantiation.codeTypeFederate get(): String = "${(eContainer() as Reactor).name}_${name}" val Instantiation.isAFederate @@ -32,21 +34,21 @@ class UcInstanceGenerator( fun generateReactorStructContainedOutputFields(inst: Instantiation) = inst.reactor.allOutputs.joinToString(separator = "\n") { with (PrependOperator) { """| - |LF_CHILD_OUTPUT_CONNECTIONS(${inst.name}, ${it.name}, ${inst.width}, ${it.width}, ${connections.getNumConnectionsFromPort(inst, it)}); - |LF_CHILD_OUTPUT_EFFECTS(${inst.name}, ${it.name}, ${inst.width}, ${it.width}, ${reactions.getParentReactionEffectsOfOutput(inst, it).size}); - |LF_CHILD_OUTPUT_OBSERVERS(${inst.name}, ${it.name}, ${inst.width}, ${it.width}, ${reactions.getParentReactionObserversOfOutput(inst, it).size}); + |LF_CHILD_OUTPUT_CONNECTIONS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${connections.getNumConnectionsFromPort(inst, it)}); + |LF_CHILD_OUTPUT_EFFECTS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionEffectsOfOutput(inst, it).size}); + |LF_CHILD_OUTPUT_OBSERVERS(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionObserversOfOutput(inst, it).size}); """.trimMargin() }} fun generateReactorStructContainedInputFields(inst: Instantiation) = inst.reactor.allInputs.joinToString(separator = "\n") { with (PrependOperator) { """| - |LF_CHILD_INPUT_SOURCES(${inst.name}, ${it.name}, ${inst.width}, ${it.width}, ${reactions.getParentReactionSourcesOfInput(inst, it).size}); + |LF_CHILD_INPUT_SOURCES(${inst.name}, ${it.name}, ${inst.codeWidth}, ${it.width}, ${reactions.getParentReactionSourcesOfInput(inst, it).size}); """.trimMargin() }} fun generateReactorStructField(inst: Instantiation) = with(PrependOperator) { """| - |LF_CHILD_REACTOR_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.width}); + |LF_CHILD_REACTOR_INSTANCE(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth}); |${generateReactorStructContainedOutputFields(inst)} |${generateReactorStructContainedInputFields(inst)} """.trimMargin() @@ -59,8 +61,8 @@ class UcInstanceGenerator( ${" |"..ports.generateDefineContainedOutputArgs(inst)} ${" |"..ports.generateDefineContainedInputArgs(inst)} |${ if (parameters.generateReactorCtorDeclArguments(inst).isNotEmpty() || ports.generateReactorCtorDeclArguments(inst).isNotEmpty()) - "LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.width} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)});" - else "LF_INITIALIZE_CHILD_REACTOR(${inst.reactor.codeType}, ${inst.name}, ${inst.width});" + "LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth} ${ports.generateReactorCtorDeclArguments(inst)} ${parameters.generateReactorCtorDeclArguments(inst)});" + else "LF_INITIALIZE_CHILD_REACTOR(${inst.reactor.codeType}, ${inst.name}, ${inst.codeWidth});" } """.trimMargin() } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index ad2c8fd6..c73fb011 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -37,6 +37,7 @@ class UcMainGenerator( |#include "${fileConfig.getReactorHeaderPath(main).toUnixString()}" |static ${main.codeType} main_reactor; |static Environment lf_environment; + |Environment *_lf_environment = &lf_environment; |void lf_exit(void) { | Environment_free(&lf_environment); |} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt index d83813c4..f11ee7b2 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcParameterGenerator.kt @@ -5,7 +5,7 @@ import org.lflang.lf.Instantiation import org.lflang.lf.Parameter import org.lflang.lf.Reactor -class UcParameterGenerator(private val reactor: Reactor) { +class UcParameterGenerator(private val reactor: Reactor, private val federate: UcFederate? = null) { companion object { @@ -33,7 +33,11 @@ class UcParameterGenerator(private val reactor: Reactor) { fun generateReactorCtorDeclArguments(r: Instantiation) = r.reactor.allParameters.joinToString(separator = "") { if (it.name == "bank_idx" || it.name == "bank_index") { - ", i" + if (federate != null) { + ", ${federate.bankIdx}" + } else { + ", i" + } } else if (r.parameters.filter{ p -> p.lhs.name == it.name}.isEmpty()) { ", ${it.init.expr.toCCode()}" } else { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt index 5824313b..9ff8775c 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt @@ -25,8 +25,7 @@ package org.lflang.generator.uc import org.lflang.* -import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcInstanceGenerator.Companion.width +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.generator.uc.UcReactorGenerator.Companion.getEffects import org.lflang.generator.uc.UcReactorGenerator.Companion.getObservers @@ -99,11 +98,11 @@ class UcPortGenerator(private val reactor: Reactor, private val connections: UcC fun generateDefineContainedOutputArgs(r: Instantiation) = r.reactor.allOutputs.joinToString(separator = "\n", prefix = "\n", postfix = "\n") { - "LF_DEFINE_CHILD_OUTPUT_ARGS(${r.name}, ${it.name}, ${r.width}, ${it.width});" + "LF_DEFINE_CHILD_OUTPUT_ARGS(${r.name}, ${it.name}, ${r.codeWidth}, ${it.width});" } fun generateDefineContainedInputArgs(r: Instantiation) = r.reactor.allInputs.joinToString(separator = "\n", prefix = "\n", postfix = "\n") { - "LF_DEFINE_CHILD_INPUT_ARGS(${r.name}, ${it.name}, ${r.width}, ${it.width});" + "LF_DEFINE_CHILD_INPUT_ARGS(${r.name}, ${it.name}, ${r.codeWidth}, ${it.width});" } fun generateReactorCtorDefArguments() = diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt index 3ed6bf1f..fb047a6e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt @@ -2,6 +2,7 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.width import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType @@ -59,7 +60,7 @@ class UcReactionGenerator(private val reactor: Reactor) { } } for (effect in allContainedEffects) { - res += effect.container.width * (effect.variable as Port).width + res += effect.container.codeWidth * (effect.variable as Port).width } return res } @@ -112,7 +113,7 @@ class UcReactionGenerator(private val reactor: Reactor) { private fun registerPortSource(varRef: VarRef, port: Port, reaction: Reaction) = if (varRef.container != null) { - (0..(varRef.container.width-1)).toList().joinToString(separator = "\n") { + (0..(varRef.container.codeWidth-1)).toList().joinToString(separator = "\n") { "LF_PORT_REGISTER_SOURCE(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" } } else { @@ -136,7 +137,7 @@ class UcReactionGenerator(private val reactor: Reactor) { private fun registerPortEffect(varRef: VarRef, port: Port, reaction: Reaction) = if (varRef.container != null) { - (0..(varRef.container.width-1)).toList().joinToString(separator = "\n") { + (0..(varRef.container.codeWidth-1)).toList().joinToString(separator = "\n") { "LF_PORT_REGISTER_EFFECT(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" } } else { @@ -153,7 +154,7 @@ class UcReactionGenerator(private val reactor: Reactor) { private fun registerPortObserver(varRef: VarRef, port: Port, reaction: Reaction) = if (varRef.container != null) { - (0..(varRef.container.width-1)).toList().joinToString(separator = "\n") { + (0..(varRef.container.codeWidth-1)).toList().joinToString(separator = "\n") { "LF_PORT_REGISTER_OBSERVER(self->${varRef.container.name}[${it}].${port.name}, ${reaction.nameInReactor}, ${port.width})" } } else { @@ -285,9 +286,9 @@ class UcReactionGenerator(private val reactor: Reactor) { |struct _${inst.reactor.codeType}_${inst.name} { ${"| "..triggers.joinToString { generateContainedTriggerInScope(it) }} |}; - |struct _${inst.reactor.codeType}_${inst.name} ${inst.name}[${inst.width}]; - |size_t ${inst.name}_width = ${inst.width}; - |for (int i = 0; i<${inst.width}; i++) { + |struct _${inst.reactor.codeType}_${inst.name} ${inst.name}[${inst.codeWidth}]; + |size_t ${inst.name}_width = ${inst.codeWidth}; + |for (int i = 0; i<${inst.codeWidth}; i++) { ${"| "..triggers.joinToString(separator = "\n") {generateContainedBankTriggerFieldInit( "${inst.name}", it)}} |} """.trimMargin() @@ -300,7 +301,7 @@ class UcReactionGenerator(private val reactor: Reactor) { private fun generateContainedTriggersAndSourcesInScope(reaction: Reaction) = reaction.allContainedEffectsTriggersAndSources.toList() .joinToString(separator = "\n") { - if (it.first.width > 1) { + if (it.first.codeWidth > 1) { generateContainedBankScope(it.second, it.first) } else { generateContainedReactorScope(it.second, it.first) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt index bf5bb0f5..263213d5 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -3,6 +3,7 @@ package org.lflang.generator.uc import org.lflang.* import org.lflang.generator.PrependOperator import org.lflang.generator.uc.UcActionGenerator.Companion.maxNumPendingEvents +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcPortGenerator.Companion.width import org.lflang.lf.* @@ -26,7 +27,7 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U return res; } - private val numChildren = reactor.allInstantiations.map { it.width }.sum() + private val numChildren = reactor.allInstantiations.map { it.codeWidth }.sum() private val parameters = UcParameterGenerator(reactor) private val connections = UcConnectionGenerator(reactor, null) @@ -76,15 +77,6 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U fun Reactor.getObservers(v: BuiltinTrigger) = allReactions.filter { it.sources.filter { it.name == v.literal }.isNotEmpty() } - - fun Reactor.getReactionQueueSize(): Int { - var res = 0 - for (child in allInstantiations) { - res += child.reactor.getReactionQueueSize() * child.width - } - res += reactions.size - return res - } } fun getMaxNumPendingEvents(): Int { diff --git a/src/environment.c b/src/environment.c index 8029ee79..99109974 100644 --- a/src/environment.c +++ b/src/environment.c @@ -49,7 +49,11 @@ interval_t Environment_get_physical_time(Environment *self) { return self->platform->get_physical_time(self->platform); } interval_t Environment_get_elapsed_physical_time(Environment *self) { - return self->platform->get_physical_time(self->platform) - self->scheduler->start_time; + if (self->scheduler->start_time == NEVER) { + return 0; + } else { + return self->platform->get_physical_time(self->platform) - self->scheduler->start_time; + } } void Environment_enter_critical_section(Environment *self) { if (self->has_async_events) { diff --git a/src/federated.c b/src/federated.c index 2376ab70..ddbb3215 100644 --- a/src/federated.c +++ b/src/federated.c @@ -7,7 +7,7 @@ void LogicalConnection_trigger_downstreams(Connection *self, const void *value, size_t value_size); void FederatedConnectionBundle_connect_to_peers(FederatedConnectionBundle **bundles, size_t bundles_size) { - LF_DEBUG(FED, "Connecting to %zu federated peers", bundles_size); + LF_INFO(FED, "Connecting to %zu federated peers", bundles_size); lf_ret_t ret; Environment *env = bundles[0]->parent->env; @@ -18,6 +18,8 @@ void FederatedConnectionBundle_connect_to_peers(FederatedConnectionBundle **bund validate(ret == LF_OK); } + LF_INFO(FED, "All connections opened"); + bool all_connected = false; interval_t wait_before_retry = FOREVER; // Initialize to maximum so we can find the lowest requested. while (!all_connected) { @@ -228,24 +230,29 @@ void FederatedConnectionBundle_handle_tagged_msg(FederatedConnectionBundle *self ret = sched->schedule_at_locked(sched, &event); switch (ret) { case LF_AFTER_STOP_TAG: - LF_WARN(FED, "Tried scheduling event after stop tag. Dropping\n"); + LF_WARN(FED, "Tried scheduling event after stop tag. Dropping"); break; case LF_PAST_TAG: - LF_ERR(FED, "Safe-to-process violation! Tried scheduling event to a past tag. Handling now instead!\n"); + LF_ERR(FED, "Safe-to-process violation! Tried scheduling event to a past tag. Handling now instead!"); event.tag = sched->current_tag(sched); event.tag.microstep++; - sched->schedule_at_locked(sched, &event); + status = sched->schedule_at_locked(sched, &event); + if (status != LF_OK) { + LF_ERR(FED, "Failed to schedule event at current tag also. Dropping"); + } else { + env->platform->new_async_event(env->platform); + } break; case LF_OK: env->platform->new_async_event(env->platform); break; default: - LF_ERR(FED, "Unknown return value `%d` from schedule_at_locked\n", ret); + LF_ERR(FED, "Unknown return value `%d` from schedule_at_locked", ret); validate(false); break; } } else { - LF_ERR(FED, "Cannot deserialize message from other Federate. Dropping\n"); + LF_ERR(FED, "Cannot deserialize message from other Federate. Dropping"); } if (lf_tag_compare(input->last_known_tag, tag) < 0) { diff --git a/src/logging.c b/src/logging.c index c99bdeec..ab6e6d46 100644 --- a/src/logging.c +++ b/src/logging.c @@ -1,5 +1,6 @@ #include "reactor-uc/logging.h" #include "reactor-uc/platform.h" +#include "reactor-uc/environment.h" #include #include @@ -22,7 +23,7 @@ void log_printf(const char *fmt, ...) { void log_message(int level, const char *module, const char *fmt, ...) { const char *level_str; switch (level) { - case LF_LOG_LEVEL_ERR: + case LF_LOG_LEVEL_ERROR: level_str = "ERROR"; break; case LF_LOG_LEVEL_WARN: @@ -44,7 +45,7 @@ void log_message(int level, const char *module, const char *fmt, ...) { #ifdef LF_COLORIZE_LOGS switch (level) { - case LF_LOG_LEVEL_ERR: + case LF_LOG_LEVEL_ERROR: log_printf(ANSI_COLOR_RED); break; case LF_LOG_LEVEL_WARN: @@ -60,7 +61,7 @@ void log_message(int level, const char *module, const char *fmt, ...) { break; } #endif - log_printf("[%s] [%s] ", level_str, module); + log_printf("%"PRId64 " [%s] [%s] ",_lf_environment->get_elapsed_physical_time(_lf_environment), level_str, module); Platform_vprintf(fmt, args); #ifdef LF_COLORIZE_LOGS log_printf(ANSI_COLOR_RESET); diff --git a/src/platform/posix/posix.c b/src/platform/posix/posix.c index 9b479eca..4973c842 100644 --- a/src/platform/posix/posix.c +++ b/src/platform/posix/posix.c @@ -8,6 +8,7 @@ static PlatformPosix platform; + static instant_t convert_timespec_to_ns(struct timespec tp) { return ((instant_t)tp.tv_sec) * BILLION + tp.tv_nsec; } void Platform_vprintf(const char *fmt, va_list args) { vprintf(fmt, args); } @@ -15,7 +16,7 @@ void Platform_vprintf(const char *fmt, va_list args) { vprintf(fmt, args); } // lf_exit should be defined in main.c and should call Environment_free, if not we provide an empty implementation here. __attribute__((weak)) void lf_exit(void) { exit(0); } -static void handle_ctrlc(int sig) { +static void handle_signal(int sig) { (void)sig; lf_exit(); exit(0); @@ -29,7 +30,8 @@ static struct timespec convert_ns_to_timespec(instant_t time) { } lf_ret_t PlatformPosix_initialize(Platform *_self) { - signal(SIGINT, handle_ctrlc); + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); PlatformPosix *self = (PlatformPosix *)_self; if (pthread_mutex_init(&self->lock, NULL) != 0) { LF_ERR(PLATFORM, "Failed to initialize mutex"); diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 785553ee..9d3ddfa4 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -38,26 +38,32 @@ static Environment *_env; static void *_TcpIpChannel_worker_thread(void *untyped_self); static void _TcpIpChannel_update_state(TcpIpChannel *self, NetworkChannelState new_state) { - TCP_IP_CHANNEL_DEBUG("Update state: %s => %s\n", NetworkChannel_state_to_string(self->state), + TCP_IP_CHANNEL_DEBUG("Update state: %s => %s", NetworkChannel_state_to_string(self->state), NetworkChannel_state_to_string(new_state)); + pthread_mutex_lock(&self->mutex); // Store old state NetworkChannelState old_state = self->state; // Update the state of the channel to its new state self->state = new_state; + pthread_mutex_unlock(&self->mutex); // Inform runtime about new state if it changed from or to NETWORK_CHANNEL_STATE_CONNECTED if ((old_state == NETWORK_CHANNEL_STATE_CONNECTED && new_state != NETWORK_CHANNEL_STATE_CONNECTED) || (old_state != NETWORK_CHANNEL_STATE_CONNECTED && new_state == NETWORK_CHANNEL_STATE_CONNECTED)) { _env->platform->new_async_event(_env->platform); } + TCP_IP_CHANNEL_DEBUG("Update state: %s => %s done", NetworkChannel_state_to_string(self->state), + NetworkChannel_state_to_string(new_state)); } static NetworkChannelState _TcpIpChannel_get_state(TcpIpChannel *self) { NetworkChannelState state; + pthread_mutex_lock(&self->mutex); state = self->state; + pthread_mutex_unlock(&self->mutex); return state; } @@ -71,6 +77,20 @@ static lf_ret_t _TcpIpChannel_reset_socket(TcpIpChannel *self) { } } + if (self->terminate_event_fds > 0) { + if (close(self->terminate_event_fds) < 0) { + TCP_IP_CHANNEL_ERR("Error closing terminate event fds=%d", errno); + return LF_ERR; + } + } + + if (self->send_failed_event_fds > 0) { + if (close(self->send_failed_event_fds) < 0) { + TCP_IP_CHANNEL_ERR("Error closing sending failed fds=%d", errno); + return LF_ERR; + } + } + if ((self->fd = socket(self->protocol_family, SOCK_STREAM, 0)) < 0) { TCP_IP_CHANNEL_ERR("Error opening socket errno=%d", errno); return LF_ERR; @@ -87,6 +107,12 @@ static lf_ret_t _TcpIpChannel_reset_socket(TcpIpChannel *self) { return LF_ERR; } + self->terminate_event_fds = eventfd(0, EFD_NONBLOCK); + if (self->terminate_event_fds == -1) { + TCP_IP_CHANNEL_ERR("Failed to initialize event file descriptor"); + return LF_ERR; + } + return LF_OK; } @@ -94,6 +120,9 @@ static void _TcpIpChannel_spawn_worker_thread(TcpIpChannel *self) { int res; TCP_IP_CHANNEL_INFO("Spawning worker thread"); + // set terminate to false so the loop runs + self->terminate = false; + memset(&self->worker_thread_stack, 0, TCP_IP_CHANNEL_RECV_THREAD_STACK_SIZE); if (pthread_attr_init(&self->worker_thread_attr) != 0) { throw("pthread_attr_init failed"); @@ -102,6 +131,10 @@ static void _TcpIpChannel_spawn_worker_thread(TcpIpChannel *self) { TCP_IP_CHANNEL_RECV_THREAD_STACK_SIZE - TCP_IP_CHANNEL_RECV_THREAD_STACK_GUARD_SIZE) < 0) { throw("pthread_attr_setstack failed"); } + + // set terminate to false so the loop runs + self->terminate = false; + res = pthread_create(&self->worker_thread, &self->worker_thread_attr, _TcpIpChannel_worker_thread, self); if (res < 0) { throw("pthread_create failed"); @@ -124,6 +157,7 @@ static lf_ret_t _TcpIpChannel_server_bind(TcpIpChannel *self) { int ret = bind(self->fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); if (ret < 0) { TCP_IP_CHANNEL_ERR("Could not bind to %s:%d errno=%d", self->host, self->port, errno); + throw("Bind failed"); _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTION_FAILED); return LF_ERR; } @@ -187,7 +221,7 @@ static lf_ret_t _TcpIpChannel_try_connect_client(NetworkChannel *untyped_self) { TCP_IP_CHANNEL_DEBUG("Connection in progress!"); return LF_OK; } else { - TCP_IP_CHANNEL_ERR("Connect failed errno=%d", errno); + TCP_IP_CHANNEL_ERR("Connect to %s:%d failed errno=%d", self->host, self->port, errno); _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTION_FAILED); return LF_ERR; } @@ -245,7 +279,7 @@ static lf_ret_t TcpIpChannel_send_blocking(NetworkChannel *untyped_self, const F switch (errno) { case ETIMEDOUT: case ENOTCONN: - ssize_t bytes_written = eventfd_write(self->send_failed_event_fds, 1); + bytes_written = eventfd_write(self->send_failed_event_fds, 1); if (bytes_written == -1) { TCP_IP_CHANNEL_ERR("Failed informing worker thread, that send_blocking failed, errno=%d", errno); } @@ -368,15 +402,16 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { TCP_IP_CHANNEL_INFO("Starting worker thread"); - // set terminate to false so the loop runs - self->terminate = false; - while (!self->terminate) { switch (_TcpIpChannel_get_state(self)) { case NETWORK_CHANNEL_STATE_OPEN: { /* try to connect */ if (self->is_server) { - _TcpIpChannel_server_bind(self); + ret = _TcpIpChannel_server_bind(self); + if (ret != LF_OK) { + _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTION_FAILED); + break; + } _TcpIpChannel_try_connect_server(untyped_self); } else { _TcpIpChannel_try_connect_client(untyped_self); @@ -409,9 +444,14 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { FD_ZERO(&readfds); FD_SET(socket, &readfds); FD_SET(self->send_failed_event_fds, &readfds); + FD_SET(self->terminate_event_fds, &readfds); // Determine the maximum file descriptor for select - max_fd = (socket > self->send_failed_event_fds) ? socket : self->send_failed_event_fds; + max_fd = socket; + if (self->send_failed_event_fds > max_fd) + max_fd = self->send_failed_event_fds; + if (self->terminate_event_fds > max_fd) + max_fd = self->terminate_event_fds; // Wait for data or cancel if send_failed externally if (select(max_fd + 1, &readfds, NULL, NULL, NULL) < 0) { @@ -433,6 +473,10 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { } else if (FD_ISSET(self->send_failed_event_fds, &readfds)) { TCP_IP_CHANNEL_DEBUG("Select -> cancelled by send_block failure"); _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_LOST_CONNECTION); + } else if (FD_ISSET(self->terminate_event_fds, &readfds)) { + TCP_IP_CHANNEL_DEBUG("Select -> cancelled by terminate event"); + self->terminate = true; + break; } } break; @@ -445,7 +489,7 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { break; } } - + TCP_IP_CHANNEL_INFO("Worker thread terminates"); return NULL; } @@ -469,6 +513,11 @@ static void TcpIpChannel_free(NetworkChannel *untyped_self) { TCP_IP_CHANNEL_DEBUG("Stopping worker thread"); err = pthread_cancel(self->worker_thread); + ssize_t bytes_written = eventfd_write(self->terminate_event_fds, 1); + if (bytes_written == -1) { + TCP_IP_CHANNEL_ERR("Failed informing worker thread, that send_blocking failed, errno=%d", errno); + } + if (err != 0) { TCP_IP_CHANNEL_ERR("Error canceling worker thread %d", err); } @@ -484,6 +533,7 @@ static void TcpIpChannel_free(NetworkChannel *untyped_self) { } } self->super.close_connection((NetworkChannel *)self); + pthread_mutex_destroy(&self->mutex); } static bool TcpIpChannel_is_connected(NetworkChannel *untyped_self) { @@ -506,6 +556,10 @@ void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, u _env = env; } + if (pthread_mutex_init(&self->mutex, NULL) != 0) { + throw("Failed to initialize mutex"); + } + self->is_server = is_server; self->terminate = true; self->protocol_family = protocol_family; diff --git a/src/platform/riot/coap_udp_ip_channel.c b/src/platform/riot/coap_udp_ip_channel.c index 89a5a2e7..5d7dc68b 100644 --- a/src/platform/riot/coap_udp_ip_channel.c +++ b/src/platform/riot/coap_udp_ip_channel.c @@ -19,7 +19,7 @@ static Environment *_env; static lf_ret_t _CoapUdpIpChannel_client_send_connect_message(CoapUdpIpChannel *self); static void _CoapUdpIpChannel_update_state(CoapUdpIpChannel *self, NetworkChannelState new_state) { - COAP_UDP_IP_CHANNEL_DEBUG("Update state: %s => %s\n", NetworkChannel_state_to_string(self->state), + COAP_UDP_IP_CHANNEL_DEBUG("Update state: %s => %s", NetworkChannel_state_to_string(self->state), NetworkChannel_state_to_string(new_state)); // Store old state @@ -40,7 +40,7 @@ static void _CoapUdpIpChannel_update_state_if_not(CoapUdpIpChannel *self, Networ // Update the state of the channel itself mutex_lock(&self->state_mutex); if (self->state != if_not) { - COAP_UDP_IP_CHANNEL_DEBUG("Update state: %d => %d\n", self->state, new_state); + COAP_UDP_IP_CHANNEL_DEBUG("Update state: %d => %d", self->state, new_state); self->state = new_state; } mutex_unlock(&self->state_mutex); diff --git a/test/lf/src/FederatedBank.lf b/test/lf/src/FederatedBank.lf index 9e190b1d..54bd279a 100644 --- a/test/lf/src/FederatedBank.lf +++ b/test/lf/src/FederatedBank.lf @@ -17,14 +17,21 @@ reactor Fed(bank_idx: int = 0) { output out: int input in: int input in2: int + state check: bool = false reaction(in) -> out {= printf("Received %d from src \n", in->value); lf_set(out, self->bank_idx); + validate(in->value == 42); =} reaction(in2) {= printf("Received %d from myself\n", in2->value); + validate(in2->value == self->bank_idx); + =} + + reaction(shutdown) {= + validate(self->check); =} } diff --git a/test/lf/src/FederatedBankMultiport.lf b/test/lf/src/FederatedBankMultiport.lf new file mode 100644 index 00000000..faec8350 --- /dev/null +++ b/test/lf/src/FederatedBankMultiport.lf @@ -0,0 +1,34 @@ +target uC { + platform: Native, + timeout: 5 sec, + logging: debug, + build-type: Release +} + +reactor Fed(bank_idx: int = 0) { + output [4] out: int + input [4] in: int + + reaction(startup) -> out {= + printf("Hello from Fed %u\n", self->bank_idx); + for (int i = 0; ibank_idx); + } + =} + + reaction(in) {= + for (int i = 0; ibank_idx == 0) { + printf("%"PRId64" Fed %u Received %d from %d \n", env->get_elapsed_logical_time(env), self->bank_idx, in[i]->value, i); + } + } + } + =} +} + + +federated reactor { + feds = new [4] Fed() + feds.out -> interleaved(feds.in) +} \ No newline at end of file From d5e95fe31121c585be8fb40c8f4eca33a7e90ff8 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 9 Jan 2025 17:12:00 +0100 Subject: [PATCH 21/65] Add @interface attr --- .../main/java/org/lflang/AttributeUtils.java | 9 +++ .../org/lflang/validation/AttributeSpec.java | 25 ++++++- .../generator/uc/UcConnectionGenerator.kt | 3 +- .../lflang/generator/uc/UcNetworkChannel.kt | 73 +++++++++++++++++-- test/lf/src/FederatedAttr.lf | 32 ++++++++ 5 files changed, 133 insertions(+), 9 deletions(-) create mode 100644 test/lf/src/FederatedAttr.lf diff --git a/lfc/core/src/main/java/org/lflang/AttributeUtils.java b/lfc/core/src/main/java/org/lflang/AttributeUtils.java index 79447cb4..df5a0107 100644 --- a/lfc/core/src/main/java/org/lflang/AttributeUtils.java +++ b/lfc/core/src/main/java/org/lflang/AttributeUtils.java @@ -276,6 +276,15 @@ public static Attribute getEnclaveAttribute(Instantiation node) { return findAttributeByName(node, "enclave"); } + // FIXME: Find ALL attributes with this name + public static List getInterfaceAttributes(Instantiation node) { + return findAttributesByName(node, "interface"); + } + + public static Attribute getLinkAttribute(Connection node) { + return findAttributeByName(node, "link"); + } + /** Return true if the specified instance has an {@code @enclave} attribute. */ public static boolean isEnclave(Instantiation node) { return getEnclaveAttribute(node) != null; diff --git a/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java b/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java index 491ff569..4fa33ff1 100644 --- a/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java +++ b/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java @@ -50,6 +50,12 @@ public class AttributeSpec { public static final String VALUE_ATTR = "value"; public static final String EACH_ATTR = "each"; public static final String OPTION_ATTR = "option"; + public static final String TYPE_ATTR = "type"; + public static final String ADDR_ATTR = "address"; + public static final String ADDRS_ATTR = "addresses"; + public static final String ARGS_ATTR = "args"; + public static final String SERVER_PORT_ATTR= "server_port"; + public static final String SERVER_SIDE_ATTR= "server_side"; /** A map from a string to a supported AttributeSpec */ public static final Map ATTRIBUTE_SPECS_BY_NAME = new HashMap<>(); @@ -243,5 +249,22 @@ enum AttrParamType { ATTRIBUTE_SPECS_BY_NAME.put( "_networkReactor", new AttributeSpec(List.of(new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false)))); - } + // @interface(type="string", address="string", args="string") e.g. @interface(type="TcpIp", address="127.0.0.1") + ATTRIBUTE_SPECS_BY_NAME.put( + "interface", + new AttributeSpec( + List.of( + new AttrParamSpec(TYPE_ATTR, AttrParamType.STRING, false), + new AttrParamSpec(ARGS_ATTR, AttrParamType.STRING, true), + new AttrParamSpec(ADDR_ATTR, AttrParamType.STRING, true)))); + // @link(type="string", server_port=int, server_side="string", args="string") e.g. @link(type="TcpIp", server_port=1042) + ATTRIBUTE_SPECS_BY_NAME.put( + "link", + new AttributeSpec( + List.of( + new AttrParamSpec(TYPE_ATTR, AttrParamType.STRING, false), + new AttrParamSpec(SERVER_PORT_ATTR, AttrParamType.INT, true), + new AttrParamSpec(ARGS_ATTR, AttrParamType.STRING, true), + new AttrParamSpec(SERVER_SIDE_ATTR, AttrParamType.STRING, true)))); +} } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 3f45da7e..99d43b5f 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -1,6 +1,7 @@ package org.lflang.generator.uc import org.lflang.* +import org.lflang.AttributeUtils.getInterfaceAttributes import org.lflang.generator.PrependOperator import org.lflang.generator.orNever import org.lflang.generator.uc.UcConnectionGenerator.Companion.networkChannelType @@ -290,7 +291,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: for (inst in top.allInstantiations) { for (bankIdx in 0..inst.width) { val fed = UcFederate(inst, bankIdx) - fed.addInterface(UcTcpIpInterface()) + createInterfacesForFederate(fed) feds.add(fed) } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt index 51305e21..6df5a4a8 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -1,20 +1,67 @@ package org.lflang.generator.uc +import org.lflang.AttributeUtils.getInterfaceAttributes import java.util.concurrent.atomic.AtomicInteger import org.lflang.generator.uc.NetworkChannelType.* +import org.lflang.lf.AttrParm +import org.lflang.lf.Attribute enum class NetworkChannelType { TcpIp } -// Singleton object to manage port numbers -object IpPortManager { +// FIXME: Ipv6? +fun addToLastOctet(ip: String, add: Int): String { + // Split the IP address into its components (octets) + val octets = ip.split(".").map { it.toInt() } + + // Increment the last octet + val newLastOctet = (octets[3] + add) % 256 // Wrap around if it exceeds 255 + + // Reconstruct the IP address with the incremented last octet + return "${octets[0]}.${octets[1]}.${octets[2]}.$newLastOctet" +} + +fun createDefaultInterface(): UcNetworkInterface = UcTcpIpInterface(globalIpPortManager) + +fun getTrimmedAttrParamString(attrParam: AttrParm) = attrParam.value.trim('"') + +fun createInterfacesForFederate(federate: UcFederate) { + val attrs: List = getInterfaceAttributes(federate.inst) + if (attrs.isEmpty()) { + federate.addInterface(createDefaultInterface()) + } else { + val ipAddressManager = IpPortManager() + for (attr in attrs) { + val params = HashSet(attr.attrParms) + val typeAttr = params.find { it.name == "type" }!! + val addressAttr = params.find { it.name == "address" } + val argsAttr = params.find { it.name == "address" } + + when (getTrimmedAttrParamString(typeAttr)) { + "TcpIp" -> { + val address = if (addressAttr != null) addToLastOctet(getTrimmedAttrParamString(addressAttr), federate.bankIdx) else null + if (address != null) { + IpAddressManager.acquireIp(address) + } + val iface = UcTcpIpInterface(IpPortManager(), address) + federate.addInterface(iface) + } + + else -> { + assert(false) + } + } + } + } +} + +class IpPortManager { private val currentPort = AtomicInteger(1024) // Starting port number private val usedPorts = mutableSetOf() @Synchronized fun acquirePortNumber(): Int { - while (true) { val port = currentPort.getAndIncrement() if (port in 1024..65535 && usedPorts.add(port)) { @@ -29,12 +76,24 @@ object IpPortManager { assert(!usedPorts.contains(port)) usedPorts.add(port) } +} +val globalIpPortManager = IpPortManager() +object IpAddressManager { + private var currentIp = 1 + private val usedIps = mutableSetOf() + + @Synchronized + fun acquireIp(ip: String) { + val ip = "192.168.0.$currentIp" + assert(usedIps.contains(ip) == false) + usedIps.add(ip) + } } abstract class UcNetworkEndpoint(val iface: UcNetworkInterface) -class UcTcpIpEndpoint(val ipAddress: String, val port: Int, iface: UcTcpIpInterface): UcNetworkEndpoint(iface) { +class UcTcpIpEndpoint(val ipAddress: String, val port: Int, iface: UcTcpIpInterface) : UcNetworkEndpoint(iface) { } @@ -43,16 +102,16 @@ abstract class UcNetworkInterface(val type: NetworkChannelType) { val endpoints = mutableListOf() } -class UcTcpIpInterface(ipAddress: String? = null) : UcNetworkInterface(TcpIp) { +class UcTcpIpInterface(private val portManager: IpPortManager, ipAddress: String? = null) : UcNetworkInterface(TcpIp) { val ipAddress = ipAddress ?: "127.0.0.1" fun createEndpoint(port: Int?): UcTcpIpEndpoint { val portNum = if (port != null) { - IpPortManager.reservePortNumber(port) + portManager.reservePortNumber(port) port } else { - IpPortManager.acquirePortNumber() + portManager.acquirePortNumber() } val ep = UcTcpIpEndpoint(ipAddress, portNum, this) endpoints.add(ep) diff --git a/test/lf/src/FederatedAttr.lf b/test/lf/src/FederatedAttr.lf new file mode 100644 index 00000000..eb1abe4b --- /dev/null +++ b/test/lf/src/FederatedAttr.lf @@ -0,0 +1,32 @@ +target uC { + platform: Native, + timeout: 1 msec +} + +reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} +} + +reactor Dst { + input in: int + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + =} +} + +federated reactor { + @interface(type="TcpIp", address="192.168.0.1") + r1 = new Src(id=42) + + @interface(type="TcpIp", address="192.168.0.2") + r2 = new Dst() + + r1.out -> r2.in +} \ No newline at end of file From 9b63d84cbe6e7a35d422fa1c9420b42b1c8d16e2 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 10 Jan 2025 08:54:04 +0100 Subject: [PATCH 22/65] More WIP --- .../main/java/org/lflang/AttributeUtils.java | 2 + .../main/java/org/lflang/LinguaFranca.xtext | 1 + .../generator/uc/UcConnectionGenerator.kt | 12 ++---- .../lflang/generator/uc/UcNetworkChannel.kt | 41 +++++++++++++++++++ test/lf/src/FederatedAttr.lf | 10 +++-- test/lf/src/FederatedBankMultiport.lf | 6 ++- 6 files changed, 59 insertions(+), 13 deletions(-) diff --git a/lfc/core/src/main/java/org/lflang/AttributeUtils.java b/lfc/core/src/main/java/org/lflang/AttributeUtils.java index df5a0107..16dfaefb 100644 --- a/lfc/core/src/main/java/org/lflang/AttributeUtils.java +++ b/lfc/core/src/main/java/org/lflang/AttributeUtils.java @@ -75,6 +75,8 @@ public static List getAttributes(EObject node) { return ((Instantiation) node).getAttributes(); } else if (node instanceof Watchdog) { return ((Watchdog) node).getAttributes(); + } else if (node instanceof Connection) { + return ((Connection) node).getAttributes(); } throw new IllegalArgumentException("Not annotatable: " + node); } diff --git a/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext b/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext index ba45b3bd..8565b3fa 100644 --- a/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext +++ b/lfc/core/src/main/java/org/lflang/LinguaFranca.xtext @@ -237,6 +237,7 @@ Instantiation: ')' (('at' host=Host ';') | ';'?); Connection: + (attributes+=Attribute)* ((leftPorts += VarRef (',' leftPorts += VarRef)*) | ( '(' leftPorts += VarRef (',' leftPorts += VarRef)* ')' iterated ?= '+'?)) ('->' | physical?='~>') diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 99d43b5f..9c0aef57 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -26,6 +26,9 @@ class UcFederate(val inst: Instantiation, val bankIdx: Int) { fun getInterface(ifaceType: NetworkChannelType): UcNetworkInterface = interfaces.find { it.type == ifaceType }!! + fun getDefaultInterface(): UcNetworkInterface = + interfaces.first()!! + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is UcFederate) return false @@ -139,14 +142,7 @@ class UcFederatedConnectionBundle( val srcIf = src.getInterface(conn.networkChannelType) val destIf = dest.getInterface(conn.networkChannelType) - networkChannel = - when (conn.networkChannelType) { - NetworkChannelType.TcpIp -> { - val srcEp = (srcIf as UcTcpIpInterface).createEndpoint(null) - val destEp = (destIf as UcTcpIpInterface).createEndpoint(null) - UcTcpIpChannel(srcEp, destEp) - } - } + networkChannel = createNetworkChannelForBundle(this) } fun numOutputs(federate: UcFederate) = groupedConnections.count { it.srcFed == federate } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt index 6df5a4a8..0b8fbea9 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -1,6 +1,7 @@ package org.lflang.generator.uc import org.lflang.AttributeUtils.getInterfaceAttributes +import org.lflang.AttributeUtils.getLinkAttribute import java.util.concurrent.atomic.AtomicInteger import org.lflang.generator.uc.NetworkChannelType.* import org.lflang.lf.AttrParm @@ -26,6 +27,46 @@ fun createDefaultInterface(): UcNetworkInterface = UcTcpIpInterface(globalIpPort fun getTrimmedAttrParamString(attrParam: AttrParm) = attrParam.value.trim('"') +fun createNetworkChannelForBundle(bundle: UcFederatedConnectionBundle): UcNetworkChannel { + val attr: Attribute? = getLinkAttribute(bundle.groupedConnections.first().lfConn) + var channel: UcNetworkChannel? + if (attr == null) { + val srcIf = bundle.src.getDefaultInterface() + val destIf = bundle.dest.getDefaultInterface() + assert(srcIf.type == destIf.type) + when (srcIf.type) { + TcpIp -> { + val srcEp = (srcIf as UcTcpIpInterface).createEndpoint(null) + val destEp = (destIf as UcTcpIpInterface).createEndpoint(null) + channel = UcTcpIpChannel(srcEp, destEp) + } + } + } else { + val params = HashSet(attr.attrParms) + val typeAttr = params.find { it.name == "type" }!! + val serverSideAttr = params.find { it.name == "server_side" } + val serverPort = params.find { it.name == "server_port" } + val args = params.find {it.name == "args"} + + when (getTrimmedAttrParamString(typeAttr)) { + "TcpIp" -> { + val srcIf = bundle.src.getInterface(TcpIp) as UcTcpIpInterface + val destIf = bundle.dest.getInterface(TcpIp) as UcTcpIpInterface + // FIXME: Verify that it is "left" or "right" + val serverLhs = if (serverSideAttr != null) getTrimmedAttrParamString(serverSideAttr) == "left" else true + val serverPort: Int? = serverPort?.value?.toInt() + channel = UcTcpIpChannel(srcIf.createEndpoint(serverPort), destIf.createEndpoint(serverPort), serverLhs) + } + else -> { + channel = null + } + } + } + + return channel!! +} + +// FIXME: Return a list fun createInterfacesForFederate(federate: UcFederate) { val attrs: List = getInterfaceAttributes(federate.inst) if (attrs.isEmpty()) { diff --git a/test/lf/src/FederatedAttr.lf b/test/lf/src/FederatedAttr.lf index eb1abe4b..7eb60d53 100644 --- a/test/lf/src/FederatedAttr.lf +++ b/test/lf/src/FederatedAttr.lf @@ -22,11 +22,15 @@ reactor Dst { } federated reactor { - @interface(type="TcpIp", address="192.168.0.1") + + @interface(type="TcpIp", name="if1", address="192.168.0.1") + // MyNetChan_ctor(MyNetChan* self); r1 = new Src(id=42) - @interface(type="TcpIp", address="192.168.0.2") - r2 = new Dst() + @interface(type="TcpIp", name="if1", address="192.168.0.1") + // MyNetChan_ctor(MyNetChan* self); + r2 = new [10] Dst() + @link(if_left=1, if_right=1, server_side="left", server_port=42) r1.out -> r2.in } \ No newline at end of file diff --git a/test/lf/src/FederatedBankMultiport.lf b/test/lf/src/FederatedBankMultiport.lf index faec8350..8a9faa96 100644 --- a/test/lf/src/FederatedBankMultiport.lf +++ b/test/lf/src/FederatedBankMultiport.lf @@ -24,11 +24,13 @@ reactor Fed(bank_idx: int = 0) { } } } - =} + =} deadline(1 msec) {= + + =} STA() } federated reactor { feds = new [4] Fed() - feds.out -> interleaved(feds.in) + feds.out ~> interleaved(feds.in) after 100 msec } \ No newline at end of file From 2f9c0f4e0980388cb7954516660dbd088bd4cb52 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Mon, 13 Jan 2025 20:22:27 +0100 Subject: [PATCH 23/65] More WIP --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05a8e253..738d05e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ if (NOT PLATFORM STREQUAL "ZEPHYR") endif() # Add compile definitions for platform and network channel specifics. -target_compile_definitions(reactor-uc PRIVATE "PLATFORM_${PLATFORM}") +target_compile_definitions(reactor-uc PUBLIC "PLATFORM_${PLATFORM}") # Add compile definition for scheduler used target_compile_definitions(reactor-uc PRIVATE "SCHEDULER_${SCHEDULER}") From 03a428c7ced615c53f38cda37b4b3afee2023517 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Mon, 13 Jan 2025 20:22:33 +0100 Subject: [PATCH 24/65] More WIP --- include/reactor-uc/network_channel.h | 31 ++ .../main/java/org/lflang/AttributeUtils.java | 11 +- .../org/lflang/validation/AttributeSpec.java | 36 +- .../generator/uc/UcCmakeFederatedGenerator.kt | 10 +- .../generator/uc/UcConnectionGenerator.kt | 72 ++-- .../generator/uc/UcFederateGenerator.kt | 24 +- .../generator/uc/UcFederatedMainGenerator.kt | 15 +- .../uc/UcFederatedPlatformGenerator.kt | 7 +- .../org/lflang/generator/uc/UcGenerator.kt | 19 +- .../lflang/generator/uc/UcMainGenerator.kt | 2 +- .../lflang/generator/uc/UcNetworkChannel.kt | 352 +++++++++++++----- .../lflang/generator/uc/UcReactorGenerator.kt | 2 +- test/lf/src/FederatedAttr.lf | 12 +- test/lf/src/FederatedAttrCustom.lf | 33 ++ test/lf/src/FederatedBankMultiport.lf | 4 +- test/lf/src/FederatedCoap.lf | 34 ++ 16 files changed, 475 insertions(+), 189 deletions(-) create mode 100644 test/lf/src/FederatedAttrCustom.lf create mode 100644 test/lf/src/FederatedCoap.lf diff --git a/include/reactor-uc/network_channel.h b/include/reactor-uc/network_channel.h index 3f0ad755..918ef27b 100644 --- a/include/reactor-uc/network_channel.h +++ b/include/reactor-uc/network_channel.h @@ -88,4 +88,35 @@ struct NetworkChannel { void (*free)(NetworkChannel *self); }; +#if defined(PLATFORM_POSIX) +#ifdef NETWORK_CHANNEL_TCP_POSIX +#include "platform/posix/tcp_ip_channel.h" +#endif + +#elif defined(PLATFORM_ZEPHYR) +#ifdef NETWORK_CHANNEL_TCP_POSIX +#include "platform/posix/tcp_ip_channel.h" +#endif + +#elif defined(PLATFORM_RIOT) +#ifdef NETWORK_CHANNEL_TCP_POSIX +#include "platform/posix/tcp_ip_channel.h" +#endif +#ifdef NETWORK_CHANNEL_COAP_RIOT +#include "platform/riot/coap_udp_ip_channel.h" +#endif + +#elif defined(PLATFORM_PICO) +#ifdef NETWORK_CHANNEL_TCP_POSIX +#error "NETWORK_POSIX_TCP not supported on PICO" +#endif + +#elif defined(PLATFORM_FLEXPRET) +#ifdef NETWORK_CHANNEL_TCP_POSIX +#error "NETWORK_POSIX_TCP not supported on FlexPRET" +#endif + +#else +#error "Platform not supported" +#endif #endif // REACTOR_UC_NETWORK_CHANNEL_H diff --git a/lfc/core/src/main/java/org/lflang/AttributeUtils.java b/lfc/core/src/main/java/org/lflang/AttributeUtils.java index 16dfaefb..741058ec 100644 --- a/lfc/core/src/main/java/org/lflang/AttributeUtils.java +++ b/lfc/core/src/main/java/org/lflang/AttributeUtils.java @@ -112,6 +112,14 @@ public static List findAttributesByName(EObject node, String name) { .toList(); } + public static List findAttributesByNameStartingWith(EObject node, String name) { + List attrs = getAttributes(node); + return attrs.stream() + .filter( + it -> + it.getAttrName().contains(name)) // case-insensitive search (more user-friendly) + .toList(); + } /** * Return the first argument specified for the attribute. * @@ -278,9 +286,8 @@ public static Attribute getEnclaveAttribute(Instantiation node) { return findAttributeByName(node, "enclave"); } - // FIXME: Find ALL attributes with this name public static List getInterfaceAttributes(Instantiation node) { - return findAttributesByName(node, "interface"); + return findAttributesByNameStartingWith(node, "interface"); } public static Attribute getLinkAttribute(Connection node) { diff --git a/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java b/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java index 4fa33ff1..2bdcaf2e 100644 --- a/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java +++ b/lfc/core/src/main/java/org/lflang/validation/AttributeSpec.java @@ -50,12 +50,6 @@ public class AttributeSpec { public static final String VALUE_ATTR = "value"; public static final String EACH_ATTR = "each"; public static final String OPTION_ATTR = "option"; - public static final String TYPE_ATTR = "type"; - public static final String ADDR_ATTR = "address"; - public static final String ADDRS_ATTR = "addresses"; - public static final String ARGS_ATTR = "args"; - public static final String SERVER_PORT_ATTR= "server_port"; - public static final String SERVER_SIDE_ATTR= "server_side"; /** A map from a string to a supported AttributeSpec */ public static final Map ATTRIBUTE_SPECS_BY_NAME = new HashMap<>(); @@ -249,22 +243,34 @@ enum AttrParamType { ATTRIBUTE_SPECS_BY_NAME.put( "_networkReactor", new AttributeSpec(List.of(new AttrParamSpec(VALUE_ATTR, AttrParamType.STRING, false)))); - // @interface(type="string", address="string", args="string") e.g. @interface(type="TcpIp", address="127.0.0.1") + // @interface:tcp(name="string", address="string") e.g. @interface:tcp(name="if1", address="127.0.0.1") ATTRIBUTE_SPECS_BY_NAME.put( - "interface", + "interface_tcp", new AttributeSpec( List.of( - new AttrParamSpec(TYPE_ATTR, AttrParamType.STRING, false), - new AttrParamSpec(ARGS_ATTR, AttrParamType.STRING, true), - new AttrParamSpec(ADDR_ATTR, AttrParamType.STRING, true)))); + new AttrParamSpec("name", AttrParamType.STRING, true), + new AttrParamSpec("address", AttrParamType.STRING, true)))); + ATTRIBUTE_SPECS_BY_NAME.put( + "interface_coap", + new AttributeSpec( + List.of( + new AttrParamSpec("name", AttrParamType.STRING, true), + new AttrParamSpec("address", AttrParamType.STRING, true)))); + ATTRIBUTE_SPECS_BY_NAME.put( + "interface_custom", + new AttributeSpec( + List.of( + new AttrParamSpec("name", AttrParamType.STRING, false), + new AttrParamSpec("args", AttrParamType.STRING, true), + new AttrParamSpec("include", AttrParamType.STRING, false)))); // @link(type="string", server_port=int, server_side="string", args="string") e.g. @link(type="TcpIp", server_port=1042) ATTRIBUTE_SPECS_BY_NAME.put( "link", new AttributeSpec( List.of( - new AttrParamSpec(TYPE_ATTR, AttrParamType.STRING, false), - new AttrParamSpec(SERVER_PORT_ATTR, AttrParamType.INT, true), - new AttrParamSpec(ARGS_ATTR, AttrParamType.STRING, true), - new AttrParamSpec(SERVER_SIDE_ATTR, AttrParamType.STRING, true)))); + new AttrParamSpec("left", AttrParamType.STRING, true), + new AttrParamSpec("right", AttrParamType.STRING, true), + new AttrParamSpec("server_port", AttrParamType.INT, true), + new AttrParamSpec("server_side", AttrParamType.STRING, true)))); } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt index 5eab2180..fa673339 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt @@ -24,7 +24,6 @@ class UcCmakeFederatedGenerator(private val federate: UcFederate, private val ta private val includeFiles = targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { fileConfig.srcPath.resolve(it).toUnixString() } private val mainTarget = federate.codeType - fun generateCmake(sources: List) = if (platform == PlatformType.Platform.NATIVE) { generateCmakePosix(sources) @@ -32,6 +31,8 @@ class UcCmakeFederatedGenerator(private val federate: UcFederate, private val ta generateCmakeEmbedded(sources) } + // TODO: Avoid code-duplication in this and posix. + // TODO: Avoid code-duplication in this and non-federated. fun generateCmakeEmbedded(sources: List) = with(PrependOperator) { """ |# This file is generated by LFC. It is meant to be included in @@ -45,7 +46,9 @@ class UcCmakeFederatedGenerator(private val federate: UcFederate, private val ta |set(LFC_GEN_INCLUDE_DIRS $S{CMAKE_CURRENT_LIST_DIR}) |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") |set(EVENT_QUEUE_SIZE ${max(numEvents, 1)} CACHE STRING "Size of the event queue") - | + |set(LFC_GEN_COMPILE_DEFS + ${" | "..federate.getCompileDefs().joinWithLn { it }} + |) """.trimMargin() } @@ -69,6 +72,9 @@ class UcCmakeFederatedGenerator(private val federate: UcFederate, private val ta | OPTIONAL |) |add_compile_definitions("LF_LOG_LEVEL_ALL=LF_LOG_LEVEL_${targetConfig.getOrDefault(LoggingProperty.INSTANCE).name.uppercase()}") + |add_compile_definitions( + ${" | "..federate.getCompileDefs().joinWithLn { it }} + |) | |add_subdirectory(reactor-uc) |target_link_libraries($S{LF_MAIN_TARGET} PRIVATE reactor-uc) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 9c0aef57..a4169aec 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -1,10 +1,8 @@ package org.lflang.generator.uc import org.lflang.* -import org.lflang.AttributeUtils.getInterfaceAttributes import org.lflang.generator.PrependOperator import org.lflang.generator.orNever -import org.lflang.generator.uc.UcConnectionGenerator.Companion.networkChannelType import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate @@ -26,9 +24,14 @@ class UcFederate(val inst: Instantiation, val bankIdx: Int) { fun getInterface(ifaceType: NetworkChannelType): UcNetworkInterface = interfaces.find { it.type == ifaceType }!! + fun getInterface(name: String): UcNetworkInterface = + interfaces.find { it.name == name}!! + fun getDefaultInterface(): UcNetworkInterface = interfaces.first()!! + fun getCompileDefs(): List = interfaces.distinctBy { it.type }.map { it.compileDefs } + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is UcFederate) return false @@ -37,6 +40,8 @@ class UcFederate(val inst: Instantiation, val bankIdx: Int) { val sameBank = bankIdx == other.bankIdx return if (isBank) sameInst && sameBank else sameInst } + + } class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { @@ -138,10 +143,6 @@ class UcFederatedConnectionBundle( val networkChannel: UcNetworkChannel init { - val conn = groupedConnections.first().lfConn - val srcIf = src.getInterface(conn.networkChannelType) - val destIf = dest.getInterface(conn.networkChannelType) - networkChannel = createNetworkChannelForBundle(this) } @@ -216,11 +217,11 @@ class UcPort(val varRef: VarRef) { fun channelsLeft(): Int = channels.size } -class UcConnectionGenerator(private val reactor: Reactor, private val federate: UcFederate?) { +class UcConnectionGenerator(private val reactor: Reactor, private val currentFederate: UcFederate?, private val allFederates: List) { private val nonFederatedConnections: List private val federatedConnectionBundles: List - private val isFederated = federate != null + private val isFederated = currentFederate != null private fun groupConnections(channels: List): List { val res = mutableListOf() @@ -276,23 +277,10 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: get(): String = this.delay.orNever().toCCode() val Connection.networkChannelType - get(): NetworkChannelType = NetworkChannelType.TcpIp + get(): NetworkChannelType = NetworkChannelType.TCP_IP + private var federateInterfacesInitialized = false private var allFederatedConnectionBundles: List = emptyList() - private var allFederates: Set = emptySet() - - - private fun createAllFederates(top: Reactor) { - val feds = mutableSetOf() - for (inst in top.allInstantiations) { - for (bankIdx in 0..inst.width) { - val fed = UcFederate(inst, bankIdx) - createInterfacesForFederate(fed) - feds.add(fed) - } - } - allFederates = feds - } private fun createFederatedConnectionBundles(groupedConnections: List) { val groupedSet = HashSet(groupedConnections) @@ -325,8 +313,12 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: init { - if (isFederated && allFederates.isEmpty()) { - createAllFederates(reactor) + if (isFederated && !federateInterfacesInitialized) { + for (fed in allFederates) { + val interfaces = createInterfacesForFederate(fed) + interfaces.forEach { fed.addInterface(it) } + } + federateInterfacesInitialized = true } // Only parse out federated connection bundles once for the very first federate @@ -344,14 +336,14 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: // Filter out the relevant bundles for this federate federatedConnectionBundles.addAll( - allFederatedConnectionBundles.filter { it.src == federate || it.dest == federate } + allFederatedConnectionBundles.filter { it.src == currentFederate || it.dest == currentFederate } ) // Add all non-federated conncetions (e.g. a loopback connection) // FIXME: How can we handle banks here? nonFederatedConnections.addAll( grouped .filterNot { it is UcFederatedGroupedConnection } - .filter { it.channels.fold(true, { acc, c -> acc && (c.src.federate == federate) }) } + .filter { it.channels.fold(true, { acc, c -> acc && (c.src.federate == currentFederate) }) } ) } else { nonFederatedConnections.addAll(grouped) @@ -379,7 +371,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: // Find all outgoing federated grouped connections from this port. for (federatedConnectionBundle in federatedConnectionBundles) { for (groupedConn in federatedConnectionBundle.groupedConnections) { - if (groupedConn.srcFed == federate && groupedConn.srcInst == instantiation && groupedConn.srcPort == port) { + if (groupedConn.srcFed == currentFederate && groupedConn.srcInst == instantiation && groupedConn.srcPort == port) { count += 1 } } @@ -412,10 +404,10 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()});" private fun generateFederatedConnectionSelfStruct(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == federate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct(conn) + if (conn.srcFed == currentFederate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct(conn) private fun generateFederatedConnectionCtor(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == federate) generateFederatedOutputCtor(conn) else generateFederatedInputCtor(conn) + if (conn.srcFed == currentFederate) generateFederatedOutputCtor(conn) else generateFederatedInputCtor(conn) private fun generateFederatedOutputInstance(conn: UcGroupedConnection) = "LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" @@ -424,7 +416,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" private fun generateFederatedConnectionInstance(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == federate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance(conn) + if (conn.srcFed == currentFederate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance(conn) private fun generateInitializeFederatedOutput(conn: UcFederatedGroupedConnection) = "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.serializeFunc});" @@ -433,7 +425,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: "LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.deserializeFunc});" private fun generateInitializeFederatedConnection(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == federate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput(conn) + if (conn.srcFed == currentFederate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput(conn) private fun generateReactorCtorCode(conn: UcGroupedConnection) = with(PrependOperator) { @@ -472,7 +464,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: private fun generateFederateConnectionStatements(conn: UcFederatedConnectionBundle) = conn.groupedConnections.joinWithLn { - if (it.srcFed == federate) { + if (it.srcFed == currentFederate) { generateConnectFederateOutputChannel(conn, it) } else { generateConnectFederateInputChannel(conn, it) @@ -523,9 +515,9 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: | FederatedConnectionBundle super; ${" | "..bundle.networkChannel.codeType} channel; ${" | "..bundle.groupedConnections.joinWithLn { generateFederatedConnectionInstance(it) }} - | LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(${bundle.numInputs(federate!!)}, ${ + | LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(${bundle.numInputs(currentFederate!!)}, ${ bundle.numOutputs( - federate!! + currentFederate!! ) }); |} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(${bundle.src.codeType}, ${bundle.dest.codeType}); @@ -536,7 +528,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: fun generateFederatedConnectionBundleCtor(bundle: UcFederatedConnectionBundle) = with(PrependOperator) { """ |LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(${bundle.src.codeType}, ${bundle.dest.codeType}) { | LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - | ${bundle.generateNetworkChannelCtor(federate!!)} + | ${bundle.generateNetworkChannelCtor(currentFederate!!)} | LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); ${" | "..bundle.groupedConnections.joinWithLn { generateInitializeFederatedConnection(it) }} |} @@ -593,11 +585,17 @@ class UcConnectionGenerator(private val reactor: Reactor, private val federate: } for (bundle in federatedConnectionBundles) { for (conn in bundle.groupedConnections) { - if (conn.destFed == federate) { + if (conn.destFed == currentFederate) { res += conn.maxNumPendingEvents } } } return res } + + fun generateNetworkChannelIncludes(): String = + federatedConnectionBundles.distinctBy { it.networkChannel.type }.joinWithLn { it.networkChannel.src.iface.includeHeaders } + + fun getNetworkChannelCompileDefs(): List = + federatedConnectionBundles.distinctBy { it.networkChannel.type }.map { it.networkChannel.src.iface.compileDefs} } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 9f00f6c5..02416617 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -5,12 +5,12 @@ import org.lflang.generator.PrependOperator import org.lflang.lf.* -class UcFederateGenerator(private val federate: UcFederate, private val fileConfig: UcFileConfig, messageReporter: MessageReporter) { +class UcFederateGenerator(private val currentFederate: UcFederate, private val otherFederates: List, private val fileConfig: UcFileConfig, messageReporter: MessageReporter) { - private val container = federate.inst.eContainer() as Reactor - private val reactor = federate.inst.reactor - private val connections = UcConnectionGenerator(container, federate) - private val parameters = UcParameterGenerator(container, federate) + private val container = currentFederate.inst.eContainer() as Reactor + private val reactor = currentFederate.inst.reactor + private val connections = UcConnectionGenerator(container, currentFederate, otherFederates) + private val parameters = UcParameterGenerator(container, currentFederate) private val ports = UcPortGenerator(container, connections) private val reactions = UcReactionGenerator(container) private val instances = UcInstanceGenerator(container, parameters, ports, connections, reactions, fileConfig, messageReporter) @@ -18,7 +18,7 @@ class UcFederateGenerator(private val federate: UcFederate, private val fileConf fun numBundles() = connections.getNumFederatedConnectionBundles() - private val includeGuard = "LFC_GEN_FEDERATE_${federate.inst.name.uppercase()}_H" + private val includeGuard = "LFC_GEN_FEDERATE_${currentFederate.inst.name.uppercase()}_H" fun getMaxNumPendingEvents(): Int { return connections.getMaxNumPendingEvents() @@ -28,11 +28,11 @@ class UcFederateGenerator(private val federate: UcFederate, private val fileConf """ |typedef struct { | Reactor super; - ${" | "..instances.generateReactorStructField(federate.inst)} + ${" | "..instances.generateReactorStructField(currentFederate.inst)} ${" | "..connections.generateReactorStructFields()} ${" | "..connections.generateFederateStructFields()} | LF_FEDERATE_BOOKKEEPING_INSTANCES(${numBundles()}); - |} ${federate.codeType}; + |} ${currentFederate.codeType}; | """.trimMargin() } @@ -41,8 +41,8 @@ class UcFederateGenerator(private val federate: UcFederate, private val fileConf """ |${generateCtorDeclaration()} { | LF_FEDERATE_CTOR_PREAMBLE(); - | LF_REACTOR_CTOR(${federate.codeType}); - ${" | "..instances.generateReactorCtorCode(federate.inst)} + | LF_REACTOR_CTOR(${currentFederate.codeType}); + ${" | "..instances.generateReactorCtorCode(currentFederate.inst)} ${" | "..connections.generateFederateCtorCodes()} ${" | "..connections.generateReactorCtorCodes()} |} @@ -50,7 +50,7 @@ class UcFederateGenerator(private val federate: UcFederate, private val fileConf """.trimMargin() } - private fun generateCtorDeclaration() = "LF_REACTOR_CTOR_SIGNATURE(${federate.codeType})" + private fun generateCtorDeclaration() = "LF_REACTOR_CTOR_SIGNATURE(${currentFederate.codeType})" fun generateHeader() = with(PrependOperator) { """ @@ -58,8 +58,8 @@ class UcFederateGenerator(private val federate: UcFederate, private val fileConf |#define ${includeGuard} |#include "reactor-uc/reactor-uc.h" |#include "${fileConfig.getReactorHeaderPath(reactor).toUnixString()}" + ${" |"..connections.generateNetworkChannelIncludes()} | - |#include "reactor-uc/platform/posix/tcp_ip_channel.h" ${" |"..connections.generateFederatedSelfStructs()} ${" |"..connections.generateSelfStructs()} ${" |"..generateFederateStruct()} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt index b5f5a5f7..885c13c5 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt @@ -9,13 +9,14 @@ import org.lflang.target.property.KeepaliveProperty import org.lflang.target.property.TimeOutProperty class UcFederatedMainGenerator( - private val main: UcFederate, + private val currentFederate: UcFederate, + private val otherFederates: List, private val targetConfig: TargetConfig, private val fileConfig: UcFileConfig, ) { - private val top = main.inst.eContainer() as Reactor - private val ucConnectionGenerator = UcConnectionGenerator(top, main) + private val top = currentFederate.inst.eContainer() as Reactor + private val ucConnectionGenerator = UcConnectionGenerator(top, currentFederate, otherFederates) fun getDuration() = if (targetConfig.isSet(TimeOutProperty.INSTANCE)) targetConfig.get(TimeOutProperty.INSTANCE).toCCode() else "FOREVER" @@ -27,7 +28,7 @@ class UcFederatedMainGenerator( """ |#include "reactor-uc/reactor-uc.h" |#include "lf_federate.h" - |static ${main.codeType} main_reactor; + |static ${currentFederate.codeType} main_reactor; |static Environment lf_environment; |Environment *_lf_environment = &lf_environment; |void lf_exit(void) { @@ -37,10 +38,10 @@ class UcFederatedMainGenerator( | Environment_ctor(&lf_environment, (Reactor *)&main_reactor); | lf_environment.scheduler->duration = ${getDuration()}; | lf_environment.scheduler->keep_alive = ${keepAlive()}; - | lf_environment.scheduler->leader = ${top.instantiations.first() == main.inst && main.bankIdx == 0}; + | lf_environment.scheduler->leader = ${top.instantiations.first() == currentFederate.inst && currentFederate.bankIdx == 0}; | lf_environment.fast_mode = ${fast()}; - | lf_environment.has_async_events = ${main.inst.reactor.inputs.isNotEmpty()}; - | ${main.codeType}_ctor(&main_reactor, NULL, &lf_environment); + | lf_environment.has_async_events = ${currentFederate.inst.reactor.inputs.isNotEmpty()}; + | ${currentFederate.codeType}_ctor(&main_reactor, NULL, &lf_environment); | lf_environment.net_bundles_size = ${ucConnectionGenerator.getNumFederatedConnectionBundles()}; | lf_environment.net_bundles = (FederatedConnectionBundle **) &main_reactor._bundles; | lf_environment.assemble(&lf_environment); diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt index 2f8fc3a5..3b17aade 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt @@ -27,13 +27,14 @@ class UcFederatedPlatformGenerator(generator: UcFederatedGenerator, private val val buildPath = srcGenPath.resolve("build") override fun generatePlatformFiles() { + val generator = generator as UcFederatedGenerator val reactorUCEnvPath = System.getenv("REACTOR_UC_PATH") if (reactorUCEnvPath == null) { messageReporter.nowhere().error("REACTOR_UC_PATH environment variable not defined. Do source env.bash in reactor-uc") return; } val runtimePath: Path = Paths.get(reactorUCEnvPath) - val mainGenerator = UcFederatedMainGenerator(federate, generator.targetConfig, generator.fileConfig) + val mainGenerator = UcFederatedMainGenerator(federate, generator.federates, generator.targetConfig, generator.fileConfig) val startSourceFile = Paths.get("lf_start.c") val startHeaderFile = Paths.get("lf_start.h") @@ -50,14 +51,14 @@ class UcFederatedPlatformGenerator(generator: UcFederatedGenerator, private val FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) FileUtil.writeToFile(mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) - val numEventsAndReactions = (generator as UcFederatedGenerator).totalNumEventsAndReactionsFederated(federate) + val numEventsAndReactions = generator.totalNumEventsAndReactionsFederated(federate) val cmakeGenerator = UcCmakeFederatedGenerator(federate, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) val makeGenerator = UcMakeGenerator(federate.inst.reactor, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) FileUtil.writeToFile(cmakeGenerator.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) val launchScriptGenerator = UcFederatedLaunchScriptGenerator(fileConfig) - FileUtil.writeToFile(launchScriptGenerator.generateLaunchScript(generator.getAllUcFederates()), fileConfig.binPath.resolve(fileConfig.name)) + FileUtil.writeToFile(launchScriptGenerator.generateLaunchScript(generator.federates), fileConfig.binPath.resolve(fileConfig.name)) fileConfig.binPath.resolve(fileConfig.name).toFile().setExecutable(true) val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index a540947f..14fc7e4a 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -98,15 +98,6 @@ abstract class UcGenerator( return res } - fun getAllUcFederates(): List { - val res = mutableListOf() - for (inst in getAllFederates()) { - for (bankIdx in 0.. { val res = mutableListOf() @@ -211,6 +202,7 @@ class UcFederatedGenerator( ) : UcGenerator(context, scopeProvider) { private val nonFederatedGenerator = UcNonFederatedGenerator(context, scopeProvider) + val federates = mutableListOf() fun totalNumEventsAndReactionsFederated(federate: UcFederate): Pair { val eventsFromFederatedConncetions = maxNumPendingEvents[mainDef.reactor]!! @@ -262,7 +254,12 @@ class UcFederatedGenerator( override fun doGenerate(resource: Resource, context: LFGeneratorContext) { super.doGenerate(resource, context) createMainDef() - for (ucFederate in getAllUcFederates()) { + for (inst in getAllFederates()) { + for (bankIdx in 0.. { - val srcEp = (srcIf as UcTcpIpInterface).createEndpoint(null) - val destEp = (destIf as UcTcpIpInterface).createEndpoint(null) - channel = UcTcpIpChannel(srcEp, destEp) + companion object { + fun isValidIPv6(address: String): Boolean { + val regex = Regex("(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4})|(::)") + return regex.matches(address) } } - } else { - val params = HashSet(attr.attrParms) - val typeAttr = params.find { it.name == "type" }!! - val serverSideAttr = params.find { it.name == "server_side" } - val serverPort = params.find { it.name == "server_port" } - val args = params.find {it.name == "args"} - - when (getTrimmedAttrParamString(typeAttr)) { - "TcpIp" -> { - val srcIf = bundle.src.getInterface(TcpIp) as UcTcpIpInterface - val destIf = bundle.dest.getInterface(TcpIp) as UcTcpIpInterface - // FIXME: Verify that it is "left" or "right" - val serverLhs = if (serverSideAttr != null) getTrimmedAttrParamString(serverSideAttr) == "left" else true - val serverPort: Int? = serverPort?.value?.toInt() - channel = UcTcpIpChannel(srcIf.createEndpoint(serverPort), destIf.createEndpoint(serverPort), serverLhs) + } + + companion object { + fun fromString(address: String): IPAddress { + return if (IPv4.isValidIPv4(address)) { + IPv4(address) + } else if (IPv6.isValidIPv6(address)) { + IPv6(address) + } else { + throw IllegalArgumentException("Invalid IP address: $address") } - else -> { - channel = null + } + + // Increment the last octet or segment of the IP + fun increment(address: IPAddress, count: Int): IPAddress { + return when (address) { + is IPv4 -> { + val octets = address.address.split(".") + val lastOctet = octets.last().toInt() + require(lastOctet < (255-count)) { "Cannot increment the last octet of an IPv4 address beyond 255" } + IPv4(octets.dropLast(1).joinToString(".") + "." + (lastOctet + count)) + } + is IPv6 -> { + val segments = address.address.split(":") + val lastSegment = BigInteger(segments.last(), 16) + val incremented = lastSegment + count.toBigInteger() + require(incremented <= BigInteger("FFFF", 16)) { "Cannot increment the last segment of an IPv6 address beyond FFFF" } + IPv6(segments.dropLast(1).joinToString(":") + ":" + incremented.toString(16)) + } } } } +} +fun getAttrParamString(attr: Attribute, param: String): String? = attr.attrParms.find {it.name == param}?.value?.trim('"') +fun getAttrParamInt(attr: Attribute, param: String): Int? = attr.attrParms.find {it.name == param}?.value?.toInt() + +fun createNetworkChannelForBundle(bundle: UcFederatedConnectionBundle): UcNetworkChannel { + val attr: Attribute? = getLinkAttribute(bundle.groupedConnections.first().lfConn) + var srcIf: UcNetworkInterface + var destIf: UcNetworkInterface + var channel: UcNetworkChannel + var serverLhs: Boolean = true + var serverPort: Int? = null + + if (attr == null) { + srcIf = bundle.src.getDefaultInterface() + destIf = bundle.dest.getDefaultInterface() + } else { + val srcIfName = getAttrParamString(attr, "left") + val destIfName = getAttrParamString(attr, "right") + val serverSideAttr = getAttrParamString(attr, "server_side") + serverPort = getAttrParamInt(attr, "server_port") + srcIf = if (srcIfName != null) bundle.src.getInterface(srcIfName!!) else bundle.src.getDefaultInterface() + destIf = if (destIfName != null) bundle.dest.getInterface(destIfName!!) else bundle.dest.getDefaultInterface() + serverLhs = if (serverSideAttr == null) true else !serverSideAttr!!.equals("right") + } + + require(srcIf.type == destIf.type) + when(srcIf.type) { + TCP_IP -> { + val srcEp = (srcIf as UcTcpIpInterface).createEndpoint(if (serverLhs) serverPort else null) + val destEp = (destIf as UcTcpIpInterface).createEndpoint(if (!serverLhs) serverPort else null) + channel = UcTcpIpChannel(srcEp, destEp, serverLhs) + } + COAP_UDP_IP -> { + val srcEp = (srcIf as UcCoapUdpIpInterface).createEndpoint() + val destEp = (destIf as UcCoapUdpIpInterface).createEndpoint() + channel = UcCoapUdpIpChannel(srcEp, destEp, serverLhs) + } + CUSTOM -> { + val srcEp = (srcIf as UcCustomInterface).createEndpoint() + val destEp = (destIf as UcCustomInterface).createEndpoint() + channel = UcCustomChannel(srcEp, destEp) + } + } + + return channel - return channel!! } -// FIXME: Return a list -fun createInterfacesForFederate(federate: UcFederate) { +fun createInterfacesForFederate(federate: UcFederate): List { val attrs: List = getInterfaceAttributes(federate.inst) if (attrs.isEmpty()) { - federate.addInterface(createDefaultInterface()) + return listOf(UcNetworkInterfaceFactory.createDefaultInterface()) } else { - val ipAddressManager = IpPortManager() - for (attr in attrs) { - val params = HashSet(attr.attrParms) - val typeAttr = params.find { it.name == "type" }!! - val addressAttr = params.find { it.name == "address" } - val argsAttr = params.find { it.name == "address" } - - when (getTrimmedAttrParamString(typeAttr)) { - "TcpIp" -> { - val address = if (addressAttr != null) addToLastOctet(getTrimmedAttrParamString(addressAttr), federate.bankIdx) else null - if (address != null) { - IpAddressManager.acquireIp(address) - } - val iface = UcTcpIpInterface(IpPortManager(), address) - federate.addInterface(iface) - } - - else -> { - assert(false) - } - } - } + return attrs.map { UcNetworkInterfaceFactory.createInterface(federate, it) } } } @@ -118,34 +155,65 @@ class IpPortManager { usedPorts.add(port) } } -val globalIpPortManager = IpPortManager() object IpAddressManager { - private var currentIp = 1 - private val usedIps = mutableSetOf() + private val usedIps = mutableSetOf() + private val portManagers = mutableMapOf() @Synchronized - fun acquireIp(ip: String) { - val ip = "192.168.0.$currentIp" - assert(usedIps.contains(ip) == false) - usedIps.add(ip) + fun acquireIp(ip: IPAddress) { + if (ip != IPAddress.fromString("127.0.0.1")) { + require(usedIps.contains(ip) == false) + usedIps.add(ip) + } + } + + fun getPortManager(ip: IPAddress): IpPortManager { + if (portManagers.contains(ip)) { + return portManagers[ip]!! + } else { + val newManager = IpPortManager() + portManagers.put(ip, newManager) + return newManager + } } } abstract class UcNetworkEndpoint(val iface: UcNetworkInterface) -class UcTcpIpEndpoint(val ipAddress: String, val port: Int, iface: UcTcpIpInterface) : UcNetworkEndpoint(iface) { +object UcNetworkInterfaceFactory { + private val creators: Map UcNetworkInterface> = mapOf( + Pair(TCP_IP, { federate, attr -> UcTcpIpInterface.fromAttribute(federate, attr) }), + Pair(COAP_UDP_IP, { federate, attr -> UcCoapUdpIpInterface.fromAttribute(federate, attr) }), + Pair(CUSTOM, { federate, attr -> UcCustomInterface.fromAttribute(federate, attr) }) + ) + fun createInterface(federate: UcFederate, attr: Attribute): UcNetworkInterface { + val protocol = attr.attrName.substringAfter("_") + when (protocol) { + "tcp" -> return creators.get(TCP_IP)!!.invoke(federate, attr) + "coap" -> return creators.get(COAP_UDP_IP)!!.invoke(federate, attr) + "custom" -> return creators.get(CUSTOM)!!.invoke(federate, attr) + else -> throw IllegalArgumentException("Unrecognized interface attribute ${attr}") + } + } + fun createDefaultInterface(): UcNetworkInterface = UcTcpIpInterface(ipAddress = IPAddress.fromString("127.0.0.1")) } -abstract class UcNetworkInterface(val type: NetworkChannelType) { - abstract fun connectTo(other: UcNetworkInterface): UcNetworkChannel +class UcTcpIpEndpoint(val ipAddress: IPAddress, val port: Int, iface: UcTcpIpInterface) : UcNetworkEndpoint(iface) {} +class UcCoapUdpIpEndpoint(val ipAddress: IPAddress, iface: UcCoapUdpIpInterface) : UcNetworkEndpoint(iface) {} +class UcCustomEndpoint(val _iface: UcCustomInterface) : UcNetworkEndpoint(_iface) {} + +abstract class UcNetworkInterface(val type: NetworkChannelType, val name: String) { val endpoints = mutableListOf() + abstract val includeHeaders: String + abstract val compileDefs: String } -class UcTcpIpInterface(private val portManager: IpPortManager, ipAddress: String? = null) : UcNetworkInterface(TcpIp) { - - val ipAddress = ipAddress ?: "127.0.0.1" +class UcTcpIpInterface(private val ipAddress: IPAddress, name: String? = null) : UcNetworkInterface(TCP_IP, name?:"tcp") { + private val portManager = IpAddressManager.getPortManager(ipAddress) + override val includeHeaders: String = "" + override val compileDefs: String= "NETWORK_CHANNEL_TCP_POSIX" fun createEndpoint(port: Int?): UcTcpIpEndpoint { val portNum = if (port != null) { @@ -159,12 +227,79 @@ class UcTcpIpInterface(private val portManager: IpPortManager, ipAddress: String return ep } - override fun connectTo(other: UcNetworkInterface): UcNetworkChannel { - TODO("Not yet implemented") + companion object { + fun fromAttribute(federate: UcFederate, attr: Attribute): UcTcpIpInterface { + val address = getAttrParamString(attr, "address") + val name = getAttrParamString(attr, "name") + val ip = if (address != null) { + var address = IPAddress.fromString(address) + + if (federate.isBank) { + address = IPAddress.increment(address, federate.bankIdx-1) + } + address + } else { + IPAddress.fromString("127.0.0.1") + } + IpAddressManager.acquireIp(ip) + return UcTcpIpInterface(ip, name) + } + } +} + +class UcCoapUdpIpInterface(private val ipAddress: IPAddress, name: String? = null) : UcNetworkInterface(COAP_UDP_IP, name?:"coap") { + private val portManager = IpAddressManager.getPortManager(ipAddress) + override val includeHeaders: String = "" + override val compileDefs: String= "NETWORK_CHANNEL_COAP_UDP" + + fun createEndpoint(): UcCoapUdpIpEndpoint { + val ep = UcCoapUdpIpEndpoint(ipAddress, this) + endpoints.add(ep) + return ep + } + + companion object { + fun fromAttribute(federate: UcFederate, attr: Attribute): UcCoapUdpIpInterface { + val address = getAttrParamString(attr, "address") + val name = getAttrParamString(attr, "name") + val ip = if (address != null) { + var address = IPAddress.fromString(address) + + if (federate.isBank) { + address = IPAddress.increment(address, federate.bankIdx-1) + } + address + } else { + IPAddress.fromString("127.0.0.1") + } + IpAddressManager.acquireIp(ip) + return UcCoapUdpIpInterface(ip, name) + } + } +} + +class UcCustomInterface(name: String, val include: String, val args: String?=null) : UcNetworkInterface(CUSTOM, name) { + override val compileDefs = "" + override val includeHeaders: String = "#include \"$include\"" + + fun createEndpoint(): UcCustomEndpoint { + val ep = UcCustomEndpoint(this) + endpoints.add(ep) + return ep + } + + companion object { + fun fromAttribute(federate: UcFederate, attr: Attribute): UcCustomInterface{ + val name = getAttrParamString(attr, "name") + val include = getAttrParamString(attr, "include") + val args = getAttrParamString(attr, "args") + return UcCustomInterface(name!!, include!!, args) + } } } abstract class UcNetworkChannel( + val type: NetworkChannelType, val src: UcNetworkEndpoint, val dest: UcNetworkEndpoint, val serverLhs: Boolean, @@ -175,17 +310,58 @@ abstract class UcNetworkChannel( } class UcTcpIpChannel( - val _src: UcTcpIpEndpoint, - val _dest: UcTcpIpEndpoint, + src: UcTcpIpEndpoint, + dest: UcTcpIpEndpoint, serverLhs: Boolean = true, -) : UcNetworkChannel(_src, _dest, serverLhs) { +) : UcNetworkChannel(TCP_IP, src, dest, serverLhs) { + val srcTcp = src + val destTcp = dest override fun generateChannelCtorSrc() = - "TcpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) _src.ipAddress else _dest.ipAddress}\", ${if (serverLhs) _src.port else _dest.port}, AF_INET, ${serverLhs});" + "TcpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${serverLhs});" override fun generateChannelCtorDest() = - "TcpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) _src.ipAddress else _dest.ipAddress}\", ${if (serverLhs) _src.port else _dest.port}, AF_INET, ${!serverLhs});" + "TcpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${!serverLhs});" override val codeType: String get() = "TcpIpChannel" } + +class UcCoapUdpIpChannel( + src: UcCoapUdpIpEndpoint, + dest: UcCoapUdpIpEndpoint, + serverLhs: Boolean = true, +) : UcNetworkChannel(COAP_UDP_IP, src, dest, serverLhs) { + val srcAddr = src.ipAddress.address + val destAddr = dest.ipAddress.address + + override fun generateChannelCtorSrc() = + "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcAddr else destAddr}\", AF_INET, ${serverLhs});" + + override fun generateChannelCtorDest() = + "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcAddr else destAddr}\" AF_INET, ${!serverLhs});" + + + override val codeType: String + get() = "CoapUdpIpChannel" +} + +class UcCustomChannel( + src: UcCustomEndpoint, + dest: UcCustomEndpoint, + serverLhs: Boolean = true, +) : UcNetworkChannel(CUSTOM, src, dest, serverLhs) { + val srcName = src.iface.name + val destName = dest.iface.name + val srcArgs = if (src._iface.args != null) ", ${src._iface.args}" else "" + val destArgs = if (dest._iface.args != null) ", ${dest._iface.args}" else "" + + override fun generateChannelCtorSrc() = + "${srcName}_ctor(&self->channel, parent->env ${srcArgs});" + + override fun generateChannelCtorDest() = + "${destName}_ctor(&self->channel, parent->env ${destArgs});" + + override val codeType: String + get() = "${srcName}" +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt index 263213d5..87e66186 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactorGenerator.kt @@ -30,7 +30,7 @@ class UcReactorGenerator(private val reactor: Reactor, private val fileConfig: U private val numChildren = reactor.allInstantiations.map { it.codeWidth }.sum() private val parameters = UcParameterGenerator(reactor) - private val connections = UcConnectionGenerator(reactor, null) + private val connections = UcConnectionGenerator(reactor, null, emptyList()) private val state = UcStateGenerator(reactor) private val ports = UcPortGenerator(reactor, connections) private val timers = UcTimerGenerator(reactor) diff --git a/test/lf/src/FederatedAttr.lf b/test/lf/src/FederatedAttr.lf index 7eb60d53..5b0fdd82 100644 --- a/test/lf/src/FederatedAttr.lf +++ b/test/lf/src/FederatedAttr.lf @@ -23,14 +23,12 @@ reactor Dst { federated reactor { - @interface(type="TcpIp", name="if1", address="192.168.0.1") - // MyNetChan_ctor(MyNetChan* self); + @interface_tcp(name="if1", address="127.0.0.1") r1 = new Src(id=42) - @interface(type="TcpIp", name="if1", address="192.168.0.1") - // MyNetChan_ctor(MyNetChan* self); - r2 = new [10] Dst() + @interface_tcp(name="if1", address="127.0.0.1") + r2 = new Dst() - @link(if_left=1, if_right=1, server_side="left", server_port=42) + @link(left="if1", right="if1", server_side="right", server_port=1042) r1.out -> r2.in -} \ No newline at end of file +} diff --git a/test/lf/src/FederatedAttrCustom.lf b/test/lf/src/FederatedAttrCustom.lf new file mode 100644 index 00000000..542d9427 --- /dev/null +++ b/test/lf/src/FederatedAttrCustom.lf @@ -0,0 +1,33 @@ +target uC { + platform: RIOT, + timeout: 1 msec +} + +reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} +} + +reactor Dst { + input in: int + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + =} +} + +federated reactor { + + @interface_custom(name="MyInterface", include="my_interface.h", args="1") + r1 = new Src(id=42) + + @interface_custom(name="MyInterface", include="my_interface.h", args="2") + r2 = new Dst() + + r1.out -> r2.in +} diff --git a/test/lf/src/FederatedBankMultiport.lf b/test/lf/src/FederatedBankMultiport.lf index 8a9faa96..2cfd86f6 100644 --- a/test/lf/src/FederatedBankMultiport.lf +++ b/test/lf/src/FederatedBankMultiport.lf @@ -24,9 +24,7 @@ reactor Fed(bank_idx: int = 0) { } } } - =} deadline(1 msec) {= - - =} STA() + =} } diff --git a/test/lf/src/FederatedCoap.lf b/test/lf/src/FederatedCoap.lf new file mode 100644 index 00000000..eec71082 --- /dev/null +++ b/test/lf/src/FederatedCoap.lf @@ -0,0 +1,34 @@ +target uC { + platform: RIOT, + timeout: 1 msec +} + +reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} +} + +reactor Dst { + input in: int + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + =} +} + +federated reactor { + + @interface_coap(name="if1", address="127.0.0.1") + r1 = new Src(id=42) + + @interface_coap(name="if1", address="127.0.0.1") + r2 = new Dst() + + @link(left="if1", right="if1", server_side="right") + r1.out -> r2.in +} From f910e5eefc0de80eb7eb808049ffd7f47c9d8e8c Mon Sep 17 00:00:00 2001 From: erlingrj Date: Mon, 13 Jan 2025 20:41:26 +0100 Subject: [PATCH 25/65] All tests are passing --- include/reactor-uc/macros.h | 4 ++-- include/reactor-uc/platform/posix/tcp_ip_channel.h | 4 ++-- src/logging.c | 2 +- src/platform/posix/posix.c | 1 - src/platform/posix/tcp_ip_channel.c | 4 ++-- test/lf/Makefile | 7 +++++++ test/lf/src/FederatedBank.lf | 10 ++++++++-- test/lf/src/FederatedBankMultiport.lf | 1 + test/lf/src/{ => only_build}/FederatedAttrCustom.lf | 0 test/lf/src/{ => only_build}/FederatedCoap.lf | 0 10 files changed, 23 insertions(+), 10 deletions(-) rename test/lf/src/{ => only_build}/FederatedAttrCustom.lf (100%) rename test/lf/src/{ => only_build}/FederatedCoap.lf (100%) diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index f2697079..5163632e 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -597,8 +597,8 @@ typedef struct FederatedOutputConnection FederatedOutputConnection; #define LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(ReactorName, OutputName, SerializeFunc) \ ReactorName##_##OutputName##_conn_ctor(&self->OutputName, self->super.parent, &self->super); \ - self->outputs[_outputs_idx] = &self->OutputName.super; \ - self->serialize_hooks[_outputs_idx] = SerializeFunc; \ + self->outputs[_outputs_idx] = &self->OutputName.super; \ + self->serialize_hooks[_outputs_idx] = SerializeFunc; \ _outputs_idx++; typedef struct FederatedInputConnection FederatedInputConnection; diff --git a/include/reactor-uc/platform/posix/tcp_ip_channel.h b/include/reactor-uc/platform/posix/tcp_ip_channel.h index d9ecb3a8..b744d8d5 100644 --- a/include/reactor-uc/platform/posix/tcp_ip_channel.h +++ b/include/reactor-uc/platform/posix/tcp_ip_channel.h @@ -25,8 +25,8 @@ struct TcpIpChannel { int fd; int client; - int send_failed_event_fds; // These file descriptors are used to signal the recv select to stop blocking - int terminate_event_fds[2]; + int send_failed_event_fds[2]; // These file descriptors are used to signal the recv select to stop blocking + int terminate_event_fds; NetworkChannelState state; pthread_mutex_t mutex; diff --git a/src/logging.c b/src/logging.c index ab6e6d46..bc41fb5d 100644 --- a/src/logging.c +++ b/src/logging.c @@ -61,7 +61,7 @@ void log_message(int level, const char *module, const char *fmt, ...) { break; } #endif - log_printf("%"PRId64 " [%s] [%s] ",_lf_environment->get_elapsed_physical_time(_lf_environment), level_str, module); + log_printf("%" PRId64 " [%s] [%s] ", _lf_environment->get_elapsed_physical_time(_lf_environment), level_str, module); Platform_vprintf(fmt, args); #ifdef LF_COLORIZE_LOGS log_printf(ANSI_COLOR_RESET); diff --git a/src/platform/posix/posix.c b/src/platform/posix/posix.c index 4973c842..05a3c932 100644 --- a/src/platform/posix/posix.c +++ b/src/platform/posix/posix.c @@ -8,7 +8,6 @@ static PlatformPosix platform; - static instant_t convert_timespec_to_ns(struct timespec tp) { return ((instant_t)tp.tv_sec) * BILLION + tp.tv_nsec; } void Platform_vprintf(const char *fmt, va_list args) { vprintf(fmt, args); } diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 2a3cb338..5b1b24a9 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -84,8 +84,8 @@ static lf_ret_t _TcpIpChannel_reset_socket(TcpIpChannel *self) { } } - if (self->send_failed_event_fds > 0) { - if (close(self->send_failed_event_fds) < 0) { + if (self->send_failed_event_fds[0] > 0) { + if (close(self->send_failed_event_fds[0]) < 0) { TCP_IP_CHANNEL_ERR("Error closing sending failed fds=%d", errno); return LF_ERR; } diff --git a/test/lf/Makefile b/test/lf/Makefile index 7250352a..54f98004 100644 --- a/test/lf/Makefile +++ b/test/lf/Makefile @@ -1,6 +1,10 @@ # Very simple Makefile script to build and compile all the LF tests. SRCS = $(wildcard src/*.lf) BINS = $(patsubst src/%.lf, bin/%, $(SRCS)) + +SRCS_ONLY_BUILD = $(wildcard src/only_build/*.lf) +BINS_ONLY_BUILD = $(patsubst src/only_build/%.lf, bin/%, $(SRCS)) + LFC_PATH=../../lfc LFC = ${LFC_PATH}/build/install/lf-cli/bin/lfc @@ -14,5 +18,8 @@ bin/%: src/%.lf ${LFC} $^ -c ./$@ +bin/%: src/only_build/%.lf + ${LFC} $^ -c + clean: rm -rf build bin src-gen \ No newline at end of file diff --git a/test/lf/src/FederatedBank.lf b/test/lf/src/FederatedBank.lf index 54bd279a..8cacfcd8 100644 --- a/test/lf/src/FederatedBank.lf +++ b/test/lf/src/FederatedBank.lf @@ -17,21 +17,27 @@ reactor Fed(bank_idx: int = 0) { output out: int input in: int input in2: int - state check: bool = false + state check1: bool = false + state check2: bool = false reaction(in) -> out {= printf("Received %d from src \n", in->value); lf_set(out, self->bank_idx); validate(in->value == 42); + validate(!self->check2); + self->check1 = true; =} reaction(in2) {= printf("Received %d from myself\n", in2->value); validate(in2->value == self->bank_idx); + validate(self->check1); + self->check2 = true; =} reaction(shutdown) {= - validate(self->check); + validate(self->check2); + validate(self->check1); =} } diff --git a/test/lf/src/FederatedBankMultiport.lf b/test/lf/src/FederatedBankMultiport.lf index 2cfd86f6..0e428d3c 100644 --- a/test/lf/src/FederatedBankMultiport.lf +++ b/test/lf/src/FederatedBankMultiport.lf @@ -19,6 +19,7 @@ reactor Fed(bank_idx: int = 0) { reaction(in) {= for (int i = 0; ivalue == i); if (self->bank_idx == 0) { printf("%"PRId64" Fed %u Received %d from %d \n", env->get_elapsed_logical_time(env), self->bank_idx, in[i]->value, i); } diff --git a/test/lf/src/FederatedAttrCustom.lf b/test/lf/src/only_build/FederatedAttrCustom.lf similarity index 100% rename from test/lf/src/FederatedAttrCustom.lf rename to test/lf/src/only_build/FederatedAttrCustom.lf diff --git a/test/lf/src/FederatedCoap.lf b/test/lf/src/only_build/FederatedCoap.lf similarity index 100% rename from test/lf/src/FederatedCoap.lf rename to test/lf/src/only_build/FederatedCoap.lf From 841549ff1c9665a850d05fd3af364f448ed02eb0 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Mon, 13 Jan 2025 21:37:35 +0100 Subject: [PATCH 26/65] Refactor --- .../generator/uc/UcConnectionGenerator.kt | 166 +++++----- .../org/lflang/generator/uc/UcExtensions.kt | 8 +- .../org/lflang/generator/uc/UcIpAddress.kt | 129 ++++++++ .../lflang/generator/uc/UcNetworkChannel.kt | 300 ++++++------------ 4 files changed, 316 insertions(+), 287 deletions(-) create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcIpAddress.kt diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index a4169aec..0393d782 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -1,6 +1,7 @@ package org.lflang.generator.uc import org.lflang.* +import org.lflang.AttributeUtils.getLinkAttribute import org.lflang.generator.PrependOperator import org.lflang.generator.orNever import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate @@ -21,14 +22,11 @@ class UcFederate(val inst: Instantiation, val bankIdx: Int) { interfaces.add(iface) } - fun getInterface(ifaceType: NetworkChannelType): UcNetworkInterface = - interfaces.find { it.type == ifaceType }!! - fun getInterface(name: String): UcNetworkInterface = - interfaces.find { it.name == name}!! + interfaces.find { it.name == name }!! fun getDefaultInterface(): UcNetworkInterface = - interfaces.first()!! + interfaces.first() fun getCompileDefs(): List = interfaces.distinctBy { it.type }.map { it.compileDefs } @@ -47,6 +45,20 @@ class UcFederate(val inst: Instantiation, val bankIdx: Int) { class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { val isFederated = (src.federate != null) && (dest.federate != null) && (src.federate != dest.federate) + fun getChannelType(): NetworkChannelType { + val linkAttr = getLinkAttribute(conn) + return if (linkAttr == null) { + src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE + } else { + val srcIf = linkAttr.getParamString("left") + if (srcIf == null) { + src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE + } else { + src.federate?.getInterface(srcIf)?.type ?: NetworkChannelType.NONE + } + } + } + companion object { fun parseConnectionChannels(conn: Connection): List { val res = mutableListOf() @@ -58,8 +70,8 @@ class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Con // Keep parsing out connections until we are out of right-hand-side (rhs) ports while (rhsPortIndex < rhsPorts.size) { // First get the current lhs and rhs port and UcGroupedConnection that we are working with - val lhsPort = lhsPorts.get(lhsPortIndex) - val rhsPort = rhsPorts.get(rhsPortIndex) + val lhsPort = lhsPorts[lhsPortIndex] + val rhsPort = rhsPorts[rhsPortIndex] if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) val lhsChannelsToAdd = lhsPort.takeRemainingChannels() @@ -82,7 +94,7 @@ class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Con // If we are out of lhs variables, but not rhs, then there should be an iterated connection. // We handle it by resetting the lhsChannels variable and index and continuing until - // we have been thorugh all rhs channels. + // we have been through all rhs channels. if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { assert(conn.isIterated) lhsPorts = conn.leftPorts.map { UcPort(it) } @@ -108,7 +120,7 @@ open class UcGroupedConnection( private var uid: Int = -1 - val bankWidth = if (srcInst != null) srcInst.codeWidth else 1 + val bankWidth = srcInst?.codeWidth ?: 1 val portWidth = srcPort.width val numDownstreams = { val frequencyMap = channels.groupingBy { Pair(it.src.getCodePortIdx(), it.src.getCodeBankIdx()) }.eachCount() @@ -119,14 +131,14 @@ open class UcGroupedConnection( fun assignUid(id: Int) { uid = id } - fun getUniqueName()= "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" + + fun getUniqueName() = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" } class UcFederatedGroupedConnection( src: VarRef, channels: List, lfConn: Connection, - uid: Int, val srcFed: UcFederate, val destFed: UcFederate, ) : UcGroupedConnection(src, channels, lfConn) { @@ -140,11 +152,7 @@ class UcFederatedConnectionBundle( val dest: UcFederate, val groupedConnections: List ) { - val networkChannel: UcNetworkChannel - - init { - networkChannel = createNetworkChannelForBundle(this) - } + val networkChannel: UcNetworkChannel = UcNetworkChannel.createNetworkChannelForBundle(this) fun numOutputs(federate: UcFederate) = groupedConnections.count { it.srcFed == federate } @@ -160,12 +168,12 @@ class UcFederatedConnectionBundle( // Convenience class around a port variable reference. It is used to encapsulate the management of multi-connections // where a single lhs port has to -class UcChannel(val varRef: VarRef, val port_idx: Int, val bank_idx: Int) { +class UcChannel(val varRef: VarRef, val portIdx: Int, val bankIdx: Int) { val federate = - if (varRef.container != null && varRef.container.isAFederate) UcFederate(varRef.container, bank_idx) else null + if (varRef.container != null && varRef.container.isAFederate) UcFederate(varRef.container, bankIdx) else null - fun getCodePortIdx() = port_idx - fun getCodeBankIdx() = if (federate == null) bank_idx else 0 + fun getCodePortIdx() = portIdx + fun getCodeBankIdx() = if (federate == null) bankIdx else 0 private val portOfContainedReactor = varRef.container != null private val reactorInstance = if (portOfContainedReactor) "${varRef.container.name}[${getCodeBankIdx()}]." else "" @@ -177,9 +185,9 @@ class UcChannel(val varRef: VarRef, val port_idx: Int, val bank_idx: Int) { // Wrapper around a variable reference to a Port. Contains a channel for each bank/multiport within it. // For each connection statement where a port is referenced, we create an UcPort and use this class // to figure out how the individual channels are connect to other UcPorts. -class UcPort(val varRef: VarRef) { - val bankWidth = varRef.container?.width ?: 1 - val portWidth = (varRef.variable as Port).width +class UcPort(private val varRef: VarRef) { + private val bankWidth = varRef.container?.width ?: 1 + private val portWidth = (varRef.variable as Port).width private val isInterleaved = varRef.isInterleaved private val channels = ArrayDeque() @@ -187,14 +195,14 @@ class UcPort(val varRef: VarRef) { // then we create channels first for ports then for banks. init { if (isInterleaved) { - for (i in 0..portWidth - 1) { - for (j in 0..bankWidth - 1) { + for (i in 0..) { +class UcConnectionGenerator( + private val reactor: Reactor, + private val currentFederate: UcFederate?, + private val allFederates: List +) { private val nonFederatedConnections: List private val federatedConnectionBundles: List @@ -237,22 +249,21 @@ class UcConnectionGenerator(private val reactor: Reactor, private val currentFed it.conn.isPhysical == c.conn.isPhysical && it.src.federate == c.src.federate && it.dest.federate == c.dest.federate && - it.conn.networkChannelType == c.conn.networkChannelType + it.getChannelType() == c.getChannelType() } - val srcFed = allFederates.find { it == UcFederate(c.src.varRef.container, c.src.bank_idx) }!! - val destFed = allFederates.find { it == UcFederate(c.dest.varRef.container, c.dest.bank_idx) }!! + val srcFed = allFederates.find { it == UcFederate(c.src.varRef.container, c.src.bankIdx) }!! + val destFed = allFederates.find { it == UcFederate(c.dest.varRef.container, c.dest.bankIdx) }!! val groupedConnection = UcFederatedGroupedConnection( c.src.varRef, grouped, c.conn, - res.size, srcFed, destFed, ) res.add(groupedConnection) - channels.removeAll(grouped) + channels.removeAll(grouped.toSet()) } else { val grouped = @@ -266,7 +277,7 @@ class UcConnectionGenerator(private val reactor: Reactor, private val currentFed val groupedConnection = UcGroupedConnection(c.src.varRef, grouped, c.conn) res.add(groupedConnection) - channels.removeAll(grouped) + channels.removeAll(grouped.toSet()) } } return res @@ -276,9 +287,6 @@ class UcConnectionGenerator(private val reactor: Reactor, private val currentFed private val Connection.delayString get(): String = this.delay.orNever().toCCode() - val Connection.networkChannelType - get(): NetworkChannelType = NetworkChannelType.TCP_IP - private var federateInterfacesInitialized = false private var allFederatedConnectionBundles: List = emptyList() @@ -305,18 +313,16 @@ class UcConnectionGenerator(private val reactor: Reactor, private val currentFed toRemove.addAll(group) } } - groupedSet.removeAll(toRemove) + groupedSet.removeAll(toRemove.toSet()) } allFederatedConnectionBundles = bundles } } - init { if (isFederated && !federateInterfacesInitialized) { for (fed in allFederates) { - val interfaces = createInterfacesForFederate(fed) - interfaces.forEach { fed.addInterface(it) } + UcNetworkInterfaceFactory.createInterfaces(fed).forEach { fed.addInterface(it) } } federateInterfacesInitialized = true } @@ -338,19 +344,20 @@ class UcConnectionGenerator(private val reactor: Reactor, private val currentFed federatedConnectionBundles.addAll( allFederatedConnectionBundles.filter { it.src == currentFederate || it.dest == currentFederate } ) - // Add all non-federated conncetions (e.g. a loopback connection) + // Add all non-federated connections (e.g. a loopback connection) // FIXME: How can we handle banks here? nonFederatedConnections.addAll( grouped .filterNot { it is UcFederatedGroupedConnection } - .filter { it.channels.fold(true, { acc, c -> acc && (c.src.federate == currentFederate) }) } + .filter { it.channels.fold(true) { acc, c -> acc && (c.src.federate == currentFederate) } } ) } else { nonFederatedConnections.addAll(grouped) } // Assign a unique ID to each connection to avoid possible naming conflicts. - val allGroupedConnections = federatedConnectionBundles.map {it.groupedConnections}.flatten().plus(nonFederatedConnections) + val allGroupedConnections = + federatedConnectionBundles.map { it.groupedConnections }.flatten().plus(nonFederatedConnections) allGroupedConnections.forEachIndexed { idx, el -> el.assignUid(idx) } @@ -404,7 +411,9 @@ class UcConnectionGenerator(private val reactor: Reactor, private val currentFed "LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.srcPort.type.toText()});" private fun generateFederatedConnectionSelfStruct(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == currentFederate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct(conn) + if (conn.srcFed == currentFederate) generateFederatedOutputSelfStruct(conn) else generateFederatedInputSelfStruct( + conn + ) private fun generateFederatedConnectionCtor(conn: UcFederatedGroupedConnection) = if (conn.srcFed == currentFederate) generateFederatedOutputCtor(conn) else generateFederatedInputCtor(conn) @@ -416,7 +425,9 @@ class UcConnectionGenerator(private val reactor: Reactor, private val currentFed "LF_FEDERATED_INPUT_CONNECTION_INSTANCE(${reactor.codeType}, ${conn.getUniqueName()});" private fun generateFederatedConnectionInstance(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == currentFederate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance(conn) + if (conn.srcFed == currentFederate) generateFederatedOutputInstance(conn) else generateFederatedInputInstance( + conn + ) private fun generateInitializeFederatedOutput(conn: UcFederatedGroupedConnection) = "LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.serializeFunc});" @@ -425,40 +436,36 @@ class UcConnectionGenerator(private val reactor: Reactor, private val currentFed "LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(${reactor.codeType}, ${conn.getUniqueName()}, ${conn.deserializeFunc});" private fun generateInitializeFederatedConnection(conn: UcFederatedGroupedConnection) = - if (conn.srcFed == currentFederate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput(conn) + if (conn.srcFed == currentFederate) generateInitializeFederatedOutput(conn) else generateInitializeFederatedInput( + conn + ) - private fun generateReactorCtorCode(conn: UcGroupedConnection) = with(PrependOperator) { - """ - |${if (conn.isLogical) "LF_INITIALIZE_LOGICAL_CONNECTION(" else "LF_INITIALIZE_DELAYED_CONNECTION("}${reactor.codeType}, ${conn.getUniqueName()}, ${conn.bankWidth}, ${conn.portWidth}); - """.trimMargin() - }; + private fun generateReactorCtorCode(conn: UcGroupedConnection) = """ + |${if (conn.isLogical) "LF_INITIALIZE_LOGICAL_CONNECTION(" else "LF_INITIALIZE_DELAYED_CONNECTION("}${reactor.codeType}, ${conn.getUniqueName()}, ${conn.bankWidth}, ${conn.portWidth}); + """.trimMargin() private fun generateFederateCtorCode(conn: UcFederatedConnectionBundle) = "LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(${conn.src.codeType}, ${conn.dest.codeType});" private fun generateConnectChannel(groupedConn: UcGroupedConnection, channel: UcConnectionChannel) = - with(PrependOperator) { - """|lf_connect((Connection *) &self->${groupedConn.getUniqueName()}[${channel.src.getCodeBankIdx()}][${channel.src.getCodePortIdx()}], (Port *) ${channel.src.generateChannelPointer()}, (Port *) ${channel.dest.generateChannelPointer()}); - """.trimMargin() - } + """|lf_connect((Connection *) &self->${groupedConn.getUniqueName()}[${channel.src.getCodeBankIdx()}][${channel.src.getCodePortIdx()}], (Port *) ${channel.src.generateChannelPointer()}, (Port *) ${channel.dest.generateChannelPointer()}); + """.trimMargin() - private fun generateConnectionStatements(conn: UcGroupedConnection) = with(PrependOperator) { - conn.channels - .joinToString(separator = "\n") { generateConnectChannel(conn, it) } - } + private fun generateConnectionStatements(conn: UcGroupedConnection) = conn.channels + .joinToString(separator = "\n") { generateConnectChannel(conn, it) } private fun generateConnectFederateOutputChannel( bundle: UcFederatedConnectionBundle, conn: UcFederatedGroupedConnection ) = conn.channels.joinWithLn { - "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${bundle.src.inst.name}[0].${it.src.varRef.name}[${it.src.port_idx}]);" + "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${bundle.src.inst.name}[0].${it.src.varRef.name}[${it.src.portIdx}]);" } private fun generateConnectFederateInputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection) = conn.channels.joinWithLn { - "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${bundle.dest.inst.name}[0].${it.dest.varRef.name}[${it.dest.port_idx}]);" + "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${bundle.dest.inst.name}[0].${it.dest.varRef.name}[${it.dest.portIdx}]);" } @@ -510,22 +517,23 @@ class UcConnectionGenerator(private val reactor: Reactor, private val currentFed else generateDelayedSelfStruct(it) } - fun generateFederatedConnectionBundleSelfStruct(bundle: UcFederatedConnectionBundle) = with(PrependOperator) { - """ |typedef struct { + private fun generateFederatedConnectionBundleSelfStruct(bundle: UcFederatedConnectionBundle) = + with(PrependOperator) { + """ |typedef struct { | FederatedConnectionBundle super; ${" | "..bundle.networkChannel.codeType} channel; ${" | "..bundle.groupedConnections.joinWithLn { generateFederatedConnectionInstance(it) }} | LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(${bundle.numInputs(currentFederate!!)}, ${ - bundle.numOutputs( - currentFederate!! - ) - }); + bundle.numOutputs( + currentFederate + ) + }); |} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(${bundle.src.codeType}, ${bundle.dest.codeType}); | """.trimMargin() - } + } - fun generateFederatedConnectionBundleCtor(bundle: UcFederatedConnectionBundle) = with(PrependOperator) { + private fun generateFederatedConnectionBundleCtor(bundle: UcFederatedConnectionBundle) = with(PrependOperator) { """ |LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(${bundle.src.codeType}, ${bundle.dest.codeType}) { | LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); | ${bundle.generateNetworkChannelCtor(currentFederate!!)} @@ -539,26 +547,26 @@ class UcConnectionGenerator(private val reactor: Reactor, private val currentFed prefix = "// Federated Connections\n", separator = "\n", postfix = "\n" - ) { - it.groupedConnections.joinToString( + ) { itOuter -> + itOuter.groupedConnections.joinToString( prefix = "// Federated input and output connection self structs\n", separator = "\n", postfix = "\n" ) { generateFederatedConnectionSelfStruct(it) } + - generateFederatedConnectionBundleSelfStruct(it) + generateFederatedConnectionBundleSelfStruct(itOuter) } fun generateFederatedCtors() = federatedConnectionBundles.joinToString( prefix = "// Federated Connections\n", separator = "\n", postfix = "\n" - ) { - it.groupedConnections.joinToString( + ) { itOuter -> + itOuter.groupedConnections.joinToString( prefix = "// Federated input and output connection constructors\n", separator = "\n", postfix = "\n" ) { generateFederatedConnectionCtor(it) } + - generateFederatedConnectionBundleCtor(it) + generateFederatedConnectionBundleCtor(itOuter) } @@ -594,8 +602,6 @@ class UcConnectionGenerator(private val reactor: Reactor, private val currentFed } fun generateNetworkChannelIncludes(): String = - federatedConnectionBundles.distinctBy { it.networkChannel.type }.joinWithLn { it.networkChannel.src.iface.includeHeaders } - - fun getNetworkChannelCompileDefs(): List = - federatedConnectionBundles.distinctBy { it.networkChannel.type }.map { it.networkChannel.src.iface.compileDefs} + federatedConnectionBundles.distinctBy { it.networkChannel.type } + .joinWithLn { it.networkChannel.src.iface.includeHeaders } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt index 8214fd67..879c7f0e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcExtensions.kt @@ -1,10 +1,7 @@ package org.lflang.generator.uc import org.lflang.* -import org.lflang.lf.BuiltinTriggerRef -import org.lflang.lf.Expression -import org.lflang.lf.TriggerRef -import org.lflang.lf.VarRef +import org.lflang.lf.* fun TimeValue.toCCode() = UcTypes.getTargetTimeExpr(this) fun Expression.toCCode(inferredType: InferredType? = null): String = @@ -26,3 +23,6 @@ val TriggerRef.name: String is BuiltinTriggerRef -> type.literal else -> unreachable() } + +fun Attribute.getParamString(param: String): String? = attrParms.find {it.name == param}?.value?.trim('"') +fun Attribute.getParamInt(param: String): Int? = attrParms.find {it.name == param}?.value?.toInt() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcIpAddress.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcIpAddress.kt new file mode 100644 index 00000000..58d680d1 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcIpAddress.kt @@ -0,0 +1,129 @@ +package org.lflang.generator.uc + +import org.lflang.AttributeUtils.getInterfaceAttributes +import org.lflang.lf.Attribute +import java.math.BigInteger +import java.util.concurrent.atomic.AtomicInteger + + +sealed class IPAddress { + abstract val address: String + + override fun toString(): String = address + override fun equals(other: Any?): Boolean { + return address == other + } + + + data class IPv4(override val address: String) : IPAddress() { + init { + require(isValidIPv4(address)) { "Invalid IPv4 address: $address" } + } + + companion object { + fun isValidIPv4(address: String): Boolean { + val regex = Regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}$") + return regex.matches(address) && + address.split(".").all { it.toIntOrNull() in 0..255 } + } + } + } + + data class IPv6(override val address: String) : IPAddress() { + init { + require(isValidIPv6(address)) { "Invalid IPv6 address: $address" } + } + + companion object { + fun isValidIPv6(address: String): Boolean { + val regex = Regex("(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4})|(::)") + return regex.matches(address) + } + } + } + + companion object { + fun fromString(address: String): IPAddress { + return if (IPv4.isValidIPv4(address)) { + IPv4(address) + } else if (IPv6.isValidIPv6(address)) { + IPv6(address) + } else { + throw IllegalArgumentException("Invalid IP address: $address") + } + } + + // Increment the last octet or segment of the IP + fun increment(address: IPAddress, count: Int): IPAddress { + return when (address) { + is IPv4 -> { + val octets = address.address.split(".") + val lastOctet = octets.last().toInt() + require(lastOctet < (255 - count)) { "Cannot increment the last octet of an IPv4 address beyond 255" } + IPv4(octets.dropLast(1).joinToString(".") + "." + (lastOctet + count)) + } + + is IPv6 -> { + val segments = address.address.split(":") + val lastSegment = BigInteger(segments.last(), 16) + val incremented = lastSegment + count.toBigInteger() + require( + incremented <= BigInteger( + "FFFF", + 16 + ) + ) { "Cannot increment the last segment of an IPv6 address beyond FFFF" } + IPv6(segments.dropLast(1).joinToString(":") + ":" + incremented.toString(16)) + } + } + } + } +} + + + +class IpPortManager { + private val currentPort = AtomicInteger(1024) // Starting port number + private val usedPorts = mutableSetOf() + + @Synchronized + fun acquirePortNumber(): Int { + while (true) { + val port = currentPort.getAndIncrement() + if (port in 1024..65535 && usedPorts.add(port)) { + return port + } + } + } + + @Synchronized + fun reservePortNumber(port: Int) { + assert(port < 65535) + assert(!usedPorts.contains(port)) + usedPorts.add(port) + } +} + +object IpAddressManager { + private val usedIps = mutableSetOf() + private val portManagers = mutableMapOf() + + @Synchronized + fun acquireIp(ip: IPAddress) { + if (ip != IPAddress.fromString("127.0.0.1")) { + require(!usedIps.contains(ip)) + usedIps.add(ip) + } + } + + fun getPortManager(ip: IPAddress): IpPortManager { + if (portManagers.contains(ip)) { + return portManagers[ip]!! + } else { + val newManager = IpPortManager() + portManagers[ip] = newManager + return newManager + } + } +} +class UcIpAddress { } \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt index eb2b9dd7..7d0038c2 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -8,195 +8,38 @@ import org.lflang.lf.Attribute import java.math.BigInteger enum class NetworkChannelType { - TCP_IP, CUSTOM, COAP_UDP_IP + TCP_IP, CUSTOM, COAP_UDP_IP, NONE } -sealed class IPAddress { - abstract val address: String - - override fun toString(): String = address - override fun equals(other: Any?): Boolean { - return address.equals(other) - } - - - data class IPv4(override val address: String) : IPAddress() { - init { - require(isValidIPv4(address)) { "Invalid IPv4 address: $address" } - } - - companion object { - fun isValidIPv4(address: String): Boolean { - val regex = Regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}$") - return regex.matches(address) && - address.split(".").all { it.toIntOrNull() in 0..255 } - } - } - } - - data class IPv6(override val address: String) : IPAddress() { - init { - require(isValidIPv6(address)) { "Invalid IPv6 address: $address" } - } - - companion object { - fun isValidIPv6(address: String): Boolean { - val regex = Regex("(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4})|(::)") - return regex.matches(address) - } - } - } - - companion object { - fun fromString(address: String): IPAddress { - return if (IPv4.isValidIPv4(address)) { - IPv4(address) - } else if (IPv6.isValidIPv6(address)) { - IPv6(address) - } else { - throw IllegalArgumentException("Invalid IP address: $address") - } - } - - // Increment the last octet or segment of the IP - fun increment(address: IPAddress, count: Int): IPAddress { - return when (address) { - is IPv4 -> { - val octets = address.address.split(".") - val lastOctet = octets.last().toInt() - require(lastOctet < (255-count)) { "Cannot increment the last octet of an IPv4 address beyond 255" } - IPv4(octets.dropLast(1).joinToString(".") + "." + (lastOctet + count)) - } - is IPv6 -> { - val segments = address.address.split(":") - val lastSegment = BigInteger(segments.last(), 16) - val incremented = lastSegment + count.toBigInteger() - require(incremented <= BigInteger("FFFF", 16)) { "Cannot increment the last segment of an IPv6 address beyond FFFF" } - IPv6(segments.dropLast(1).joinToString(":") + ":" + incremented.toString(16)) - } - } - } - } -} -fun getAttrParamString(attr: Attribute, param: String): String? = attr.attrParms.find {it.name == param}?.value?.trim('"') -fun getAttrParamInt(attr: Attribute, param: String): Int? = attr.attrParms.find {it.name == param}?.value?.toInt() - -fun createNetworkChannelForBundle(bundle: UcFederatedConnectionBundle): UcNetworkChannel { - val attr: Attribute? = getLinkAttribute(bundle.groupedConnections.first().lfConn) - var srcIf: UcNetworkInterface - var destIf: UcNetworkInterface - var channel: UcNetworkChannel - var serverLhs: Boolean = true - var serverPort: Int? = null - - if (attr == null) { - srcIf = bundle.src.getDefaultInterface() - destIf = bundle.dest.getDefaultInterface() - } else { - val srcIfName = getAttrParamString(attr, "left") - val destIfName = getAttrParamString(attr, "right") - val serverSideAttr = getAttrParamString(attr, "server_side") - serverPort = getAttrParamInt(attr, "server_port") - srcIf = if (srcIfName != null) bundle.src.getInterface(srcIfName!!) else bundle.src.getDefaultInterface() - destIf = if (destIfName != null) bundle.dest.getInterface(destIfName!!) else bundle.dest.getDefaultInterface() - serverLhs = if (serverSideAttr == null) true else !serverSideAttr!!.equals("right") - } - - require(srcIf.type == destIf.type) - when(srcIf.type) { - TCP_IP -> { - val srcEp = (srcIf as UcTcpIpInterface).createEndpoint(if (serverLhs) serverPort else null) - val destEp = (destIf as UcTcpIpInterface).createEndpoint(if (!serverLhs) serverPort else null) - channel = UcTcpIpChannel(srcEp, destEp, serverLhs) - } - COAP_UDP_IP -> { - val srcEp = (srcIf as UcCoapUdpIpInterface).createEndpoint() - val destEp = (destIf as UcCoapUdpIpInterface).createEndpoint() - channel = UcCoapUdpIpChannel(srcEp, destEp, serverLhs) - } - CUSTOM -> { - val srcEp = (srcIf as UcCustomInterface).createEndpoint() - val destEp = (destIf as UcCustomInterface).createEndpoint() - channel = UcCustomChannel(srcEp, destEp) - } - } - - return channel - -} - -fun createInterfacesForFederate(federate: UcFederate): List { - val attrs: List = getInterfaceAttributes(federate.inst) - if (attrs.isEmpty()) { - return listOf(UcNetworkInterfaceFactory.createDefaultInterface()) - } else { - return attrs.map { UcNetworkInterfaceFactory.createInterface(federate, it) } - } -} - -class IpPortManager { - private val currentPort = AtomicInteger(1024) // Starting port number - private val usedPorts = mutableSetOf() - - @Synchronized - fun acquirePortNumber(): Int { - while (true) { - val port = currentPort.getAndIncrement() - if (port in 1024..65535 && usedPorts.add(port)) { - return port - } - } - } - - @Synchronized - fun reservePortNumber(port: Int) { - assert(port < 65535) - assert(!usedPorts.contains(port)) - usedPorts.add(port) - } -} - -object IpAddressManager { - private val usedIps = mutableSetOf() - private val portManagers = mutableMapOf() - - @Synchronized - fun acquireIp(ip: IPAddress) { - if (ip != IPAddress.fromString("127.0.0.1")) { - require(usedIps.contains(ip) == false) - usedIps.add(ip) - } - } +abstract class UcNetworkEndpoint(val iface: UcNetworkInterface) - fun getPortManager(ip: IPAddress): IpPortManager { - if (portManagers.contains(ip)) { - return portManagers[ip]!! +object UcNetworkInterfaceFactory { + private val creators: Map UcNetworkInterface> = + mapOf( + Pair(TCP_IP, { federate, attr -> UcTcpIpInterface.fromAttribute(federate, attr) }), + Pair(COAP_UDP_IP, { federate, attr -> UcCoapUdpIpInterface.fromAttribute(federate, attr) }), + Pair(CUSTOM, { federate, attr -> UcCustomInterface.fromAttribute(federate, attr) }) + ) + + fun createInterfaces(federate: UcFederate): List { + val attrs: List = getInterfaceAttributes(federate.inst) + return if (attrs.isEmpty()) { + listOf(createDefaultInterface()) } else { - val newManager = IpPortManager() - portManagers.put(ip, newManager) - return newManager + attrs.map { createInterface(federate, it) } } } -} - -abstract class UcNetworkEndpoint(val iface: UcNetworkInterface) - -object UcNetworkInterfaceFactory { - private val creators: Map UcNetworkInterface> = mapOf( - Pair(TCP_IP, { federate, attr -> UcTcpIpInterface.fromAttribute(federate, attr) }), - Pair(COAP_UDP_IP, { federate, attr -> UcCoapUdpIpInterface.fromAttribute(federate, attr) }), - Pair(CUSTOM, { federate, attr -> UcCustomInterface.fromAttribute(federate, attr) }) - ) fun createInterface(federate: UcFederate, attr: Attribute): UcNetworkInterface { val protocol = attr.attrName.substringAfter("_") - when (protocol) { - "tcp" -> return creators.get(TCP_IP)!!.invoke(federate, attr) - "coap" -> return creators.get(COAP_UDP_IP)!!.invoke(federate, attr) - "custom" -> return creators.get(CUSTOM)!!.invoke(federate, attr) - else -> throw IllegalArgumentException("Unrecognized interface attribute ${attr}") + return when (protocol) { + "tcp" -> creators.get(TCP_IP)!!.invoke(federate, attr) + "coap" -> creators.get(COAP_UDP_IP)!!.invoke(federate, attr) + "custom" -> creators.get(CUSTOM)!!.invoke(federate, attr) + else -> throw IllegalArgumentException("Unrecognized interface attribute $attr") } } + fun createDefaultInterface(): UcNetworkInterface = UcTcpIpInterface(ipAddress = IPAddress.fromString("127.0.0.1")) } @@ -210,10 +53,11 @@ abstract class UcNetworkInterface(val type: NetworkChannelType, val name: String abstract val compileDefs: String } -class UcTcpIpInterface(private val ipAddress: IPAddress, name: String? = null) : UcNetworkInterface(TCP_IP, name?:"tcp") { +class UcTcpIpInterface(private val ipAddress: IPAddress, name: String? = null) : + UcNetworkInterface(TCP_IP, name ?: "tcp") { private val portManager = IpAddressManager.getPortManager(ipAddress) override val includeHeaders: String = "" - override val compileDefs: String= "NETWORK_CHANNEL_TCP_POSIX" + override val compileDefs: String = "NETWORK_CHANNEL_TCP_POSIX" fun createEndpoint(port: Int?): UcTcpIpEndpoint { val portNum = if (port != null) { @@ -229,13 +73,13 @@ class UcTcpIpInterface(private val ipAddress: IPAddress, name: String? = null) : companion object { fun fromAttribute(federate: UcFederate, attr: Attribute): UcTcpIpInterface { - val address = getAttrParamString(attr, "address") - val name = getAttrParamString(attr, "name") + val address = attr.getParamString("address") + val name = attr.getParamString("name") val ip = if (address != null) { var address = IPAddress.fromString(address) if (federate.isBank) { - address = IPAddress.increment(address, federate.bankIdx-1) + address = IPAddress.increment(address, federate.bankIdx - 1) } address } else { @@ -247,10 +91,10 @@ class UcTcpIpInterface(private val ipAddress: IPAddress, name: String? = null) : } } -class UcCoapUdpIpInterface(private val ipAddress: IPAddress, name: String? = null) : UcNetworkInterface(COAP_UDP_IP, name?:"coap") { - private val portManager = IpAddressManager.getPortManager(ipAddress) +class UcCoapUdpIpInterface(private val ipAddress: IPAddress, name: String? = null) : + UcNetworkInterface(COAP_UDP_IP, name ?: "coap") { override val includeHeaders: String = "" - override val compileDefs: String= "NETWORK_CHANNEL_COAP_UDP" + override val compileDefs: String = "NETWORK_CHANNEL_COAP_UDP" fun createEndpoint(): UcCoapUdpIpEndpoint { val ep = UcCoapUdpIpEndpoint(ipAddress, this) @@ -260,13 +104,13 @@ class UcCoapUdpIpInterface(private val ipAddress: IPAddress, name: String? = nul companion object { fun fromAttribute(federate: UcFederate, attr: Attribute): UcCoapUdpIpInterface { - val address = getAttrParamString(attr, "address") - val name = getAttrParamString(attr, "name") + val address = attr.getParamString("address") + val name = attr.getParamString("name") val ip = if (address != null) { var address = IPAddress.fromString(address) if (federate.isBank) { - address = IPAddress.increment(address, federate.bankIdx-1) + address = IPAddress.increment(address, federate.bankIdx - 1) } address } else { @@ -278,7 +122,8 @@ class UcCoapUdpIpInterface(private val ipAddress: IPAddress, name: String? = nul } } -class UcCustomInterface(name: String, val include: String, val args: String?=null) : UcNetworkInterface(CUSTOM, name) { +class UcCustomInterface(name: String, val include: String, val args: String? = null) : + UcNetworkInterface(CUSTOM, name) { override val compileDefs = "" override val includeHeaders: String = "#include \"$include\"" @@ -289,10 +134,10 @@ class UcCustomInterface(name: String, val include: String, val args: String?=nul } companion object { - fun fromAttribute(federate: UcFederate, attr: Attribute): UcCustomInterface{ - val name = getAttrParamString(attr, "name") - val include = getAttrParamString(attr, "include") - val args = getAttrParamString(attr, "args") + fun fromAttribute(federate: UcFederate, attr: Attribute): UcCustomInterface { + val name = attr.getParamString("name") + val include = attr.getParamString("include") + val args = attr.getParamString("args") return UcCustomInterface(name!!, include!!, args) } } @@ -307,6 +152,55 @@ abstract class UcNetworkChannel( abstract fun generateChannelCtorSrc(): String abstract fun generateChannelCtorDest(): String abstract val codeType: String + + companion object { + fun createNetworkChannelForBundle(bundle: UcFederatedConnectionBundle): UcNetworkChannel { + val attr: Attribute? = getLinkAttribute(bundle.groupedConnections.first().lfConn) + var srcIf: UcNetworkInterface + var destIf: UcNetworkInterface + var channel: UcNetworkChannel + var serverLhs: Boolean = true + var serverPort: Int? = null + + if (attr == null) { + srcIf = bundle.src.getDefaultInterface() + destIf = bundle.dest.getDefaultInterface() + } else { + val srcIfName = attr.getParamString("left") + val destIfName = attr.getParamString("right") + val serverSideAttr = attr.getParamString("server_side") + serverPort = attr.getParamInt("server_port") + srcIf = + if (srcIfName != null) bundle.src.getInterface(srcIfName!!) else bundle.src.getDefaultInterface() + destIf = + if (destIfName != null) bundle.dest.getInterface(destIfName!!) else bundle.dest.getDefaultInterface() + serverLhs = if (serverSideAttr == null) true else !serverSideAttr!!.equals("right") + } + + require(srcIf.type == destIf.type) + when (srcIf.type) { + TCP_IP -> { + val srcEp = (srcIf as UcTcpIpInterface).createEndpoint(if (serverLhs) serverPort else null) + val destEp = (destIf as UcTcpIpInterface).createEndpoint(if (!serverLhs) serverPort else null) + channel = UcTcpIpChannel(srcEp, destEp, serverLhs) + } + + COAP_UDP_IP -> { + val srcEp = (srcIf as UcCoapUdpIpInterface).createEndpoint() + val destEp = (destIf as UcCoapUdpIpInterface).createEndpoint() + channel = UcCoapUdpIpChannel(srcEp, destEp, serverLhs) + } + + CUSTOM -> { + val srcEp = (srcIf as UcCustomInterface).createEndpoint() + val destEp = (destIf as UcCustomInterface).createEndpoint() + channel = UcCustomChannel(srcEp, destEp) + } + NONE -> throw IllegalArgumentException("Tried creating network channel with type=NONE") + } + return channel + } + } } class UcTcpIpChannel( @@ -314,8 +208,8 @@ class UcTcpIpChannel( dest: UcTcpIpEndpoint, serverLhs: Boolean = true, ) : UcNetworkChannel(TCP_IP, src, dest, serverLhs) { - val srcTcp = src - val destTcp = dest + private val srcTcp = src + private val destTcp = dest override fun generateChannelCtorSrc() = "TcpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${serverLhs});" @@ -332,8 +226,8 @@ class UcCoapUdpIpChannel( dest: UcCoapUdpIpEndpoint, serverLhs: Boolean = true, ) : UcNetworkChannel(COAP_UDP_IP, src, dest, serverLhs) { - val srcAddr = src.ipAddress.address - val destAddr = dest.ipAddress.address + private val srcAddr = src.ipAddress.address + private val destAddr = dest.ipAddress.address override fun generateChannelCtorSrc() = "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcAddr else destAddr}\", AF_INET, ${serverLhs});" @@ -351,10 +245,10 @@ class UcCustomChannel( dest: UcCustomEndpoint, serverLhs: Boolean = true, ) : UcNetworkChannel(CUSTOM, src, dest, serverLhs) { - val srcName = src.iface.name - val destName = dest.iface.name - val srcArgs = if (src._iface.args != null) ", ${src._iface.args}" else "" - val destArgs = if (dest._iface.args != null) ", ${dest._iface.args}" else "" + private val srcName = src.iface.name + private val destName = dest.iface.name + private val srcArgs = if (src._iface.args != null) ", ${src._iface.args}" else "" + private val destArgs = if (dest._iface.args != null) ", ${dest._iface.args}" else "" override fun generateChannelCtorSrc() = "${srcName}_ctor(&self->channel, parent->env ${srcArgs});" @@ -363,5 +257,5 @@ class UcCustomChannel( "${destName}_ctor(&self->channel, parent->env ${destArgs});" override val codeType: String - get() = "${srcName}" + get() = srcName } From d3173cfc04c8e701494ee6c2d1c951ae8ae3f268 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Tue, 14 Jan 2025 11:54:28 +0100 Subject: [PATCH 27/65] Formatting --- .../generator/uc/UcConnectionGenerator.kt | 283 ++++-------------- .../lflang/generator/uc/UcConnectionUtils.kt | 147 +++++++++ .../org/lflang/generator/uc/UcFederate.kt | 33 ++ .../lflang/generator/uc/UcPortGenerator.kt | 4 +- .../generator/uc/UcReactionGenerator.kt | 4 +- .../uc/UcStandalonePlatformGenerator.kt | 1 - test/lf/src/FederatedConnection.lf | 9 +- 7 files changed, 254 insertions(+), 227 deletions(-) create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 0393d782..012f7723 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -1,230 +1,12 @@ package org.lflang.generator.uc import org.lflang.* -import org.lflang.AttributeUtils.getLinkAttribute import org.lflang.generator.PrependOperator import org.lflang.generator.orNever -import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate -import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate -import org.lflang.generator.uc.UcInstanceGenerator.Companion.width -import org.lflang.generator.uc.UcPortGenerator.Companion.width import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.lf.* -class UcFederate(val inst: Instantiation, val bankIdx: Int) { - val isBank = inst.isBank - private val interfaces = mutableListOf() - - val codeType = if (isBank) "${inst.codeTypeFederate}_${bankIdx}" else inst.codeTypeFederate - - fun addInterface(iface: UcNetworkInterface) { - interfaces.add(iface) - } - - fun getInterface(name: String): UcNetworkInterface = - interfaces.find { it.name == name }!! - - fun getDefaultInterface(): UcNetworkInterface = - interfaces.first() - - fun getCompileDefs(): List = interfaces.distinctBy { it.type }.map { it.compileDefs } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is UcFederate) return false - - val sameInst = inst == other.inst - val sameBank = bankIdx == other.bankIdx - return if (isBank) sameInst && sameBank else sameInst - } - - -} - -class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { - val isFederated = (src.federate != null) && (dest.federate != null) && (src.federate != dest.federate) - - fun getChannelType(): NetworkChannelType { - val linkAttr = getLinkAttribute(conn) - return if (linkAttr == null) { - src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE - } else { - val srcIf = linkAttr.getParamString("left") - if (srcIf == null) { - src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE - } else { - src.federate?.getInterface(srcIf)?.type ?: NetworkChannelType.NONE - } - } - } - - companion object { - fun parseConnectionChannels(conn: Connection): List { - val res = mutableListOf() - val rhsPorts = conn.rightPorts.map { UcPort(it) } - var rhsPortIndex = 0 - var lhsPorts = conn.leftPorts.map { UcPort(it) } - var lhsPortIndex = 0 - - // Keep parsing out connections until we are out of right-hand-side (rhs) ports - while (rhsPortIndex < rhsPorts.size) { - // First get the current lhs and rhs port and UcGroupedConnection that we are working with - val lhsPort = lhsPorts[lhsPortIndex] - val rhsPort = rhsPorts[rhsPortIndex] - if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { - val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) - val lhsChannelsToAdd = lhsPort.takeRemainingChannels() - lhsChannelsToAdd.zip(rhsChannelsToAdd) - .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } - lhsPortIndex += 1 - } else if (rhsPort.channelsLeft() < lhsPort.channelsLeft()) { - val numRhsChannelsToAdd = rhsPort.channelsLeft() - val rhsChannelsToAdd = rhsPort.takeRemainingChannels() - val lhsChannelsToAdd = lhsPort.takeChannels(numRhsChannelsToAdd) - lhsChannelsToAdd.zip(rhsChannelsToAdd) - .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } - rhsPortIndex += 1 - } else { - lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()) - .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } - rhsPortIndex += 1 - lhsPortIndex += 1 - } - - // If we are out of lhs variables, but not rhs, then there should be an iterated connection. - // We handle it by resetting the lhsChannels variable and index and continuing until - // we have been through all rhs channels. - if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { - assert(conn.isIterated) - lhsPorts = conn.leftPorts.map { UcPort(it) } - lhsPortIndex = 0 - } - } - return res - } - } -} - -open class UcGroupedConnection( - val src: VarRef, - val channels: List, - val lfConn: Connection, -) { - val delay = lfConn.delay.orNever().toCCode() - val isPhysical = lfConn.isPhysical - val isLogical = !lfConn.isPhysical && lfConn.delay == null - val srcInst = src.container - val srcPort = src.variable as Port - val isDelayed = lfConn.isPhysical || !isLogical // We define physical connections as delayed. - - private var uid: Int = -1 - - val bankWidth = srcInst?.codeWidth ?: 1 - val portWidth = srcPort.width - val numDownstreams = { - val frequencyMap = channels.groupingBy { Pair(it.src.getCodePortIdx(), it.src.getCodeBankIdx()) }.eachCount() - frequencyMap.values.maxOrNull() ?: 0 - } - val maxNumPendingEvents = 16 // FIXME: Must be derived from the program - - fun assignUid(id: Int) { - uid = id - } - - fun getUniqueName() = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" -} - -class UcFederatedGroupedConnection( - src: VarRef, - channels: List, - lfConn: Connection, - val srcFed: UcFederate, - val destFed: UcFederate, -) : UcGroupedConnection(src, channels, lfConn) { - - val serializeFunc = "serialize_payload_default" - val deserializeFunc = "deserialize_payload_default" -} - -class UcFederatedConnectionBundle( - val src: UcFederate, - val dest: UcFederate, - val groupedConnections: List -) { - val networkChannel: UcNetworkChannel = UcNetworkChannel.createNetworkChannelForBundle(this) - - fun numOutputs(federate: UcFederate) = groupedConnections.count { it.srcFed == federate } - - fun numInputs(federate: UcFederate) = groupedConnections.count { it.destFed == federate } - - fun generateNetworkChannelCtor(federate: UcFederate): String = - if (federate == src) { - networkChannel.generateChannelCtorSrc() - } else { - networkChannel.generateChannelCtorDest() - } -} - -// Convenience class around a port variable reference. It is used to encapsulate the management of multi-connections -// where a single lhs port has to -class UcChannel(val varRef: VarRef, val portIdx: Int, val bankIdx: Int) { - val federate = - if (varRef.container != null && varRef.container.isAFederate) UcFederate(varRef.container, bankIdx) else null - - fun getCodePortIdx() = portIdx - fun getCodeBankIdx() = if (federate == null) bankIdx else 0 - - private val portOfContainedReactor = varRef.container != null - private val reactorInstance = if (portOfContainedReactor) "${varRef.container.name}[${getCodeBankIdx()}]." else "" - private val portInstance = "${varRef.name}[${getCodePortIdx()}]" - - fun generateChannelPointer() = "&self->${reactorInstance}${portInstance}" -} - -// Wrapper around a variable reference to a Port. Contains a channel for each bank/multiport within it. -// For each connection statement where a port is referenced, we create an UcPort and use this class -// to figure out how the individual channels are connect to other UcPorts. -class UcPort(private val varRef: VarRef) { - private val bankWidth = varRef.container?.width ?: 1 - private val portWidth = (varRef.variable as Port).width - private val isInterleaved = varRef.isInterleaved - private val channels = ArrayDeque() - - // Construct the stack of channels belonging to this port. If this port is interleaved, - // then we create channels first for ports then for banks. - init { - if (isInterleaved) { - for (i in 0.. = takeChannels(channels.size) - - // Get a number of channels from this port. This has sideeffects and will remove these - // channels from the port. - fun takeChannels(numChannels: Int): List { - assert(numChannels >= channels.size) - val res = mutableListOf() - for (i in 1..numChannels) { - res.add(channels.removeFirst()) - } - return res - } - - fun channelsLeft(): Int = channels.size -} - class UcConnectionGenerator( private val reactor: Reactor, private val currentFederate: UcFederate?, @@ -283,6 +65,63 @@ class UcConnectionGenerator( return res } + private fun getUcPorts(portVarRefs: List, federates: List): List { + return portVarRefs.map { c -> + if (c.container?.isAFederate ?: false) { + val federates = allFederates.filter { it.inst == c.container } + UcPort(c, federates) + } else { + UcPort(c, emptyList()) + } + } + + } + + private fun parseConnectionChannels(conn: Connection, federates: List): List { + val res = mutableListOf() + val rhsPorts = getUcPorts(conn.rightPorts, federates) + var rhsPortIndex = 0 + + var lhsPorts = getUcPorts(conn.leftPorts, federates) + var lhsPortIndex = 0 + + // Keep parsing out connections until we are out of right-hand-side (rhs) ports + while (rhsPortIndex < rhsPorts.size) { + // First get the current lhs and rhs port and UcGroupedConnection that we are working with + val lhsPort = lhsPorts[lhsPortIndex] + val rhsPort = rhsPorts[rhsPortIndex] + if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { + val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) + val lhsChannelsToAdd = lhsPort.takeRemainingChannels() + lhsChannelsToAdd.zip(rhsChannelsToAdd) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } + lhsPortIndex += 1 + } else if (rhsPort.channelsLeft() < lhsPort.channelsLeft()) { + val numRhsChannelsToAdd = rhsPort.channelsLeft() + val rhsChannelsToAdd = rhsPort.takeRemainingChannels() + val lhsChannelsToAdd = lhsPort.takeChannels(numRhsChannelsToAdd) + lhsChannelsToAdd.zip(rhsChannelsToAdd) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } + rhsPortIndex += 1 + } else { + lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } + rhsPortIndex += 1 + lhsPortIndex += 1 + } + + // If we are out of lhs variables, but not rhs, then there should be an iterated connection. + // We handle it by resetting the lhsChannels variable and index and continuing until + // we have been through all rhs channels. + if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { + assert(conn.isIterated) + lhsPorts = getUcPorts(conn.leftPorts, federates) + lhsPortIndex = 0 + } + } + return res + } + companion object { private val Connection.delayString get(): String = this.delay.orNever().toCCode() @@ -329,7 +168,7 @@ class UcConnectionGenerator( // Only parse out federated connection bundles once for the very first federate val channels = mutableListOf() - reactor.allConnections.forEach { channels.addAll(UcConnectionChannel.parseConnectionChannels(it)) } + reactor.allConnections.forEach { channels.addAll(parseConnectionChannels(it, allFederates)) } val grouped = groupConnections(channels) nonFederatedConnections = mutableListOf() federatedConnectionBundles = mutableListOf() @@ -345,7 +184,6 @@ class UcConnectionGenerator( allFederatedConnectionBundles.filter { it.src == currentFederate || it.dest == currentFederate } ) // Add all non-federated connections (e.g. a loopback connection) - // FIXME: How can we handle banks here? nonFederatedConnections.addAll( grouped .filterNot { it is UcFederatedGroupedConnection } @@ -463,7 +301,10 @@ class UcConnectionGenerator( "lf_connect_federated_output((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${bundle.src.inst.name}[0].${it.src.varRef.name}[${it.src.portIdx}]);" } - private fun generateConnectFederateInputChannel(bundle: UcFederatedConnectionBundle, conn: UcGroupedConnection) = + private fun generateConnectFederateInputChannel( + bundle: UcFederatedConnectionBundle, + conn: UcGroupedConnection + ) = conn.channels.joinWithLn { "lf_connect_federated_input((Connection *)&self->LF_FEDERATED_CONNECTION_BUNDLE_NAME(${bundle.src.codeType}, ${bundle.dest.codeType}).${conn.getUniqueName()}, (Port*) &self->${bundle.dest.inst.name}[0].${it.dest.varRef.name}[${it.dest.portIdx}]);" } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt new file mode 100644 index 00000000..7e180af2 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt @@ -0,0 +1,147 @@ +package org.lflang.generator.uc + +import org.lflang.AttributeUtils.getLinkAttribute +import org.lflang.generator.orNever +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeWidth +import org.lflang.generator.uc.UcInstanceGenerator.Companion.width +import org.lflang.generator.uc.UcPortGenerator.Companion.width +import org.lflang.lf.Connection +import org.lflang.lf.Port +import org.lflang.lf.VarRef + + +class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { + val isFederated = (src.federate != null) && (dest.federate != null) && (src.federate != dest.federate) + + fun getChannelType(): NetworkChannelType { + val linkAttr = getLinkAttribute(conn) + return if (linkAttr == null) { + src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE + } else { + val srcIf = linkAttr.getParamString("left") + if (srcIf == null) { + src.federate?.getDefaultInterface()?.type ?: NetworkChannelType.NONE + } else { + src.federate?.getInterface(srcIf)?.type ?: NetworkChannelType.NONE + } + } + } +} + +open class UcGroupedConnection( + val src: VarRef, + val channels: List, + val lfConn: Connection, +) { + val delay = lfConn.delay.orNever().toCCode() + val isPhysical = lfConn.isPhysical + val isLogical = !lfConn.isPhysical && lfConn.delay == null + val srcInst = src.container + val srcPort = src.variable as Port + val isDelayed = lfConn.isPhysical || !isLogical // We define physical connections as delayed. + + private var uid: Int = -1 + + val bankWidth = srcInst?.codeWidth ?: 1 + val portWidth = srcPort.width + val numDownstreams = { + val frequencyMap = + channels.groupingBy { Pair(it.src.getCodePortIdx(), it.src.getCodeBankIdx()) }.eachCount() + frequencyMap.values.maxOrNull() ?: 0 + } + val maxNumPendingEvents = 16 // FIXME: Must be derived from the program + + fun assignUid(id: Int) { + uid = id + } + + fun getUniqueName() = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" +} + +class UcFederatedGroupedConnection( + src: VarRef, + channels: List, + lfConn: Connection, + val srcFed: UcFederate, + val destFed: UcFederate, +) : UcGroupedConnection(src, channels, lfConn) { + + val serializeFunc = "serialize_payload_default" + val deserializeFunc = "deserialize_payload_default" +} + +class UcFederatedConnectionBundle( + val src: UcFederate, + val dest: UcFederate, + val groupedConnections: List +) { + val networkChannel: UcNetworkChannel = UcNetworkChannel.createNetworkChannelForBundle(this) + + fun numOutputs(federate: UcFederate) = groupedConnections.count { it.srcFed == federate } + + fun numInputs(federate: UcFederate) = groupedConnections.count { it.destFed == federate } + + fun generateNetworkChannelCtor(federate: UcFederate): String = + if (federate == src) { + networkChannel.generateChannelCtorSrc() + } else { + networkChannel.generateChannelCtorDest() + } +} + +// Convenience class around a port variable reference. It is used to encapsulate the management of multi-connections +// where a single lhs port has to +class UcChannel(val varRef: VarRef, val portIdx: Int, val bankIdx: Int, val federate: UcFederate?) { + fun getCodePortIdx() = portIdx + fun getCodeBankIdx() = if (federate == null) bankIdx else 0 + + private val portOfContainedReactor = varRef.container != null + private val reactorInstance = + if (portOfContainedReactor) "${varRef.container.name}[${getCodeBankIdx()}]." else "" + private val portInstance = "${varRef.name}[${getCodePortIdx()}]" + + fun generateChannelPointer() = "&self->${reactorInstance}${portInstance}" +} + +// Wrapper around a variable reference to a Port. Contains a channel for each bank/multiport within it. +// For each connection statement where a port is referenced, we create an UcPort and use this class +// to figure out how the individual channels are connect to other UcPorts. +class UcPort(varRef: VarRef, federates: List) { + private val bankWidth = varRef.container?.width ?: 1 + private val portWidth = (varRef.variable as Port).width + private val isInterleaved = varRef.isInterleaved + private val channels = ArrayDeque() + + // Construct the stack of channels belonging to this port. If this port is interleaved, + // then we create channels first for ports then for banks. + init { + if (isInterleaved) { + for (i in 0.. = takeChannels(channels.size) + + // Get a number of channels from this port. This has sideeffects and will remove these + // channels from the port. + fun takeChannels(numChannels: Int): List { + assert(numChannels >= channels.size) + val res = mutableListOf() + for (i in 1..numChannels) { + res.add(channels.removeFirst()) + } + return res + } + + fun channelsLeft(): Int = channels.size +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt new file mode 100644 index 00000000..e729eb9c --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederate.kt @@ -0,0 +1,33 @@ +package org.lflang.generator.uc + +import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate +import org.lflang.isBank +import org.lflang.lf.Instantiation + +class UcFederate(val inst: Instantiation, val bankIdx: Int) { + val isBank = inst.isBank + private val interfaces = mutableListOf() + + val codeType = if (isBank) "${inst.codeTypeFederate}_${bankIdx}" else inst.codeTypeFederate + + fun addInterface(iface: UcNetworkInterface) { + interfaces.add(iface) + } + + fun getInterface(name: String): UcNetworkInterface = + interfaces.find { it.name == name }!! + + fun getDefaultInterface(): UcNetworkInterface = + interfaces.first() + + fun getCompileDefs(): List = interfaces.distinctBy { it.type }.map { it.compileDefs } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is UcFederate) return false + + val sameInst = inst == other.inst + val sameBank = bankIdx == other.bankIdx + return if (isBank) sameInst && sameBank else sameInst + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt index 9ff8775c..3e45ddba 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPortGenerator.kt @@ -80,7 +80,7 @@ class UcPortGenerator(private val reactor: Reactor, private val connections: UcC when (it) { is Input -> generateInputCtor(it) is Output -> generateOutputCtor(it) - else -> "" // FIXME: Runtime exception + else -> throw IllegalArgumentException("Error: Port was neither input nor output") } } @@ -91,7 +91,7 @@ class UcPortGenerator(private val reactor: Reactor, private val connections: UcC when(port) { is Input -> generateReactorCtorCode(port) is Output -> generateReactorCtorCode(port) - else -> "" // FIXME: Runtime exception + else -> throw IllegalArgumentException("Error: Port was neither input nor output") } fun generateReactorCtorCodes() = reactor.allInputs.plus(reactor.allOutputs).joinToString(prefix = "// Initialize ports\n", separator = "\n", postfix = "\n") { generateReactorCtorCode(it)} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt index fb047a6e..e05b9e90 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcReactionGenerator.kt @@ -240,9 +240,9 @@ class UcReactionGenerator(private val reactor: Reactor) { private fun generateContainedTriggerInScope(trigger: VarRef) = if (trigger.variable.isMultiport) { - "LF_MULTIPORT_PTR_INSTANCE(${trigger.container.reactor.codeType}, ${trigger.name}, ${(trigger.variable as Port).width});" // FIXME: What about this? + "LF_MULTIPORT_PTR_INSTANCE(${trigger.container.reactor.codeType}, ${trigger.name}, ${(trigger.variable as Port).width});" } else { - "LF_PORT_PTR_INSTANCE(${trigger.container.reactor.codeType}, ${trigger.name});" // FIXME: What about this? + "LF_PORT_PTR_INSTANCE(${trigger.container.reactor.codeType}, ${trigger.name});" } private fun generateContainedMultiportTriggerFieldInit(instName: String, containerName: String, trigger: VarRef, port: Port) = with(PrependOperator) { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt index f8b0755c..63b88ad4 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt @@ -25,7 +25,6 @@ class UcStandalonePlatformGenerator(generator: UcGenerator, val srcGenPath: Path override fun generatePlatformFiles() { val reactorUCEnvPath = System.getenv("REACTOR_UC_PATH") - // FIXME: Improve this error handling if (reactorUCEnvPath == null) { messageReporter.nowhere().error("REACTOR_UC_PATH environment variable not defined. Do source env.bash in reactor-uc") return; diff --git a/test/lf/src/FederatedConnection.lf b/test/lf/src/FederatedConnection.lf index d56ebc29..5bd958b7 100644 --- a/test/lf/src/FederatedConnection.lf +++ b/test/lf/src/FederatedConnection.lf @@ -1,6 +1,5 @@ target uC { platform: Native, - timeout: 1 msec } reactor Src(id: int = 0) { @@ -13,11 +12,19 @@ reactor Src(id: int = 0) { reactor Dst { input in: int + state check: bool = false reaction(startup) {= printf("Hello from Dst!\n"); =} reaction(in) {= printf("Received %d from Src\n", in->value); + validate(in->value == 42); + self->check = true; + env->request_shutdown(env); + =} + + reaction(shutdown) {= + validate(self->check); =} } From dd618448e134d63fa53916df7333e9d01575685d Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 15:38:50 +0100 Subject: [PATCH 28/65] Refactorings --- .../org/lflang/generator/LFGenerator.java | 7 +- .../generator/uc/UcCmakeFederatedGenerator.kt | 85 ------ .../lflang/generator/uc/UcCmakeGenerator.kt | 80 +++--- .../generator/uc/UcConnectionGenerator.kt | 130 +++++---- .../lflang/generator/uc/UcConnectionUtils.kt | 43 ++- .../generator/uc/UcFederateGenerator.kt | 5 +- .../uc/UcFederatedLaunchScriptGenerator.kt | 26 +- .../generator/uc/UcFederatedMainGenerator.kt | 74 ----- .../uc/UcFederatedPlatformGenerator.kt | 207 ------------- .../org/lflang/generator/uc/UcFileConfig.kt | 3 - .../org/lflang/generator/uc/UcGenerator.kt | 272 ++---------------- .../generator/uc/UcGeneratorFederated.kt | 160 +++++++++++ .../generator/uc/UcGeneratorNonFederated.kt | 94 ++++++ .../org/lflang/generator/uc/UcIpAddress.kt | 1 + .../lflang/generator/uc/UcMainGenerator.kt | 94 +++--- .../lflang/generator/uc/UcMakeGenerator.kt | 10 +- .../lflang/generator/uc/UcNetworkChannel.kt | 60 ++-- .../generator/uc/UcPlatformGenerator.kt | 187 +++++++++++- .../uc/UcPlatformGeneratorFederated.kt | 30 ++ .../uc/UcPlatformGeneratorNonFederated.kt | 29 ++ .../uc/UcStandalonePlatformGenerator.kt | 198 ------------- test/lf/src/FederatedAttr.lf | 2 +- test/lf/src/FederatedBank.lf | 4 +- test/lf/src/FederatedConnection.lf | 1 + 24 files changed, 790 insertions(+), 1012 deletions(-) delete mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt delete mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt delete mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorNonFederated.kt create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt create mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt delete mode 100644 lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt diff --git a/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java b/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java index 38d2e729..f3b24150 100644 --- a/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java +++ b/lfc/core/src/main/java/org/lflang/generator/LFGenerator.java @@ -2,20 +2,17 @@ import com.google.inject.Inject; import com.google.inject.Injector; -import java.io.IOException; + import java.nio.file.Path; -import java.util.Arrays; + import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xtext.generator.AbstractGenerator; import org.eclipse.xtext.generator.IFileSystemAccess2; import org.eclipse.xtext.generator.IGeneratorContext; -import org.eclipse.xtext.util.RuntimeIOException; import org.lflang.FileConfig; import org.lflang.MessageReporter; import org.lflang.ast.ASTUtils; import org.lflang.generator.uc.UcFileConfig; -import org.lflang.generator.uc.UcGenerator; -import org.lflang.generator.uc.UcNonFederatedGenerator; import org.lflang.scoping.LFGlobalScopeProvider; import org.lflang.target.Target; diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt deleted file mode 100644 index fa673339..00000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeFederatedGenerator.kt +++ /dev/null @@ -1,85 +0,0 @@ - -package org.lflang.generator.uc - -import org.lflang.* -import org.lflang.target.TargetConfig -import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate -import org.lflang.lf.Instantiation -import org.lflang.lf.Reactor -import org.lflang.target.property.BuildTypeProperty -import org.lflang.target.property.CmakeIncludeProperty -import org.lflang.target.property.LoggingProperty -import org.lflang.target.property.PlatformProperty -import org.lflang.target.property.type.PlatformType -import org.lflang.util.FileUtil -import java.nio.file.Path -import java.time.LocalDateTime -import kotlin.io.path.name -import kotlin.math.max - -class UcCmakeFederatedGenerator(private val federate: UcFederate, private val targetConfig: TargetConfig, private val fileConfig: FileConfig, private val numEvents: Int, private val numReactions: Int) { - private val S = '$' // a little trick to escape the dollar sign with $S - private val platform = targetConfig.get(PlatformProperty.INSTANCE).platform - private val includeFiles = targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { fileConfig.srcPath.resolve(it).toUnixString() } - private val mainTarget = federate.codeType - - fun generateCmake(sources: List) = - if (platform == PlatformType.Platform.NATIVE) { - generateCmakePosix(sources) - } else { - generateCmakeEmbedded(sources) - } - - // TODO: Avoid code-duplication in this and posix. - // TODO: Avoid code-duplication in this and non-federated. - fun generateCmakeEmbedded(sources: List) = with(PrependOperator) { - """ - |# This file is generated by LFC. It is meant to be included in - |# an existing CMake project. - | - |set(LFC_GEN_SOURCES - ${" | "..sources.filterNot{it.name == "lf_main.c"}.joinWithLn { "$S{CMAKE_CURRENT_LIST_DIR}/${it.toUnixString()}"}} - |) - |set(LFC_GEN_MAIN "$S{CMAKE_CURRENT_LIST_DIR}/lf_main.c") - |set(REACTOR_UC_PATH $S{CMAKE_CURRENT_LIST_DIR}/reactor-uc) - |set(LFC_GEN_INCLUDE_DIRS $S{CMAKE_CURRENT_LIST_DIR}) - |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") - |set(EVENT_QUEUE_SIZE ${max(numEvents, 1)} CACHE STRING "Size of the event queue") - |set(LFC_GEN_COMPILE_DEFS - ${" | "..federate.getCompileDefs().joinWithLn { it }} - |) - """.trimMargin() - } - - fun generateCmakePosix(sources: List) = with(PrependOperator) { - """ - |cmake_minimum_required(VERSION 3.10) - |project(${mainTarget} LANGUAGES C) - |set(PLATFORM POSIX CACHE STRING "Target platform") - |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") - |set(EVENT_QUEUE_SIZE ${max(numEvents, 1)} CACHE STRING "Size of the event queue") - |set(CMAKE_BUILD_TYPE ${targetConfig.getOrDefault(BuildTypeProperty.INSTANCE)}) - |set(NETWORK_CHANNEL_TCP_POSIX ON CACHE BOOL "Use TcpIpChannel") - | - |set(LF_MAIN_TARGET ${mainTarget}) - |set(SOURCES - ${" | "..sources.joinWithLn { it.toUnixString() }} - |) - |add_executable($S{LF_MAIN_TARGET} $S{SOURCES}) - |install(TARGETS $S{LF_MAIN_TARGET} - | RUNTIME DESTINATION $S{CMAKE_INSTALL_BINDIR} - | OPTIONAL - |) - |add_compile_definitions("LF_LOG_LEVEL_ALL=LF_LOG_LEVEL_${targetConfig.getOrDefault(LoggingProperty.INSTANCE).name.uppercase()}") - |add_compile_definitions( - ${" | "..federate.getCompileDefs().joinWithLn { it }} - |) - | - |add_subdirectory(reactor-uc) - |target_link_libraries($S{LF_MAIN_TARGET} PRIVATE reactor-uc) - |target_include_directories($S{LF_MAIN_TARGET} PRIVATE $S{CMAKE_CURRENT_LIST_DIR}) - ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")} - """.trimMargin() - } -} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index f25a5daf..8b5c30d7 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -18,61 +18,71 @@ import java.time.LocalDateTime import kotlin.io.path.name import kotlin.math.max -class UcCmakeGenerator(private val mainDef: Instantiation, private val targetConfig: TargetConfig, private val fileConfig: FileConfig, private val numEvents: Int, private val numReactions: Int) { - private val S = '$' // a little trick to escape the dollar sign with $S - private val platform = targetConfig.get(PlatformProperty.INSTANCE).platform - private val includeFiles = targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { fileConfig.srcPath.resolve(it).toUnixString() } - private val mainTarget = fileConfig.name +abstract class UcCmakeGenerator(private val targetConfig: TargetConfig, private val fileConfig: UcFileConfig, private val numEvents: Int, private val numReactions: Int) { + protected val S = '$' // a little trick to escape the dollar sign with $S + private val minCmakeVersion = "3.10" + protected val includeFiles = targetConfig.get(CmakeIncludeProperty.INSTANCE)?.map { fileConfig.srcPath.resolve(it).toUnixString() } + protected val platform = targetConfig.get(PlatformProperty.INSTANCE).platform + abstract val mainTarget: String + abstract fun generateCmake(sources: List): String - - fun generateCmake(sources: List) = - if (platform == PlatformType.Platform.NATIVE) { - generateCmakePosix(sources) - } else { - generateCmakeEmbedded(sources) - } - - fun generateCmakeEmbedded(sources: List) = with(PrependOperator) { - """ - |# This file is generated by LFC. It is meant to be included in - |# an existing CMake project. - | + protected fun generateCmakeCommon(sources: List, compileDefs: List) = with(PrependOperator) { + """ | |set(LFC_GEN_SOURCES ${" | "..sources.filterNot{it.name == "lf_main.c"}.joinWithLn { "$S{CMAKE_CURRENT_LIST_DIR}/${it.toUnixString()}"}} + |) + |set(LFC_GEN_COMPILE_DEFS + ${" | "..compileDefs.joinWithLn { it }} |) |set(LFC_GEN_MAIN "$S{CMAKE_CURRENT_LIST_DIR}/lf_main.c") |set(REACTOR_UC_PATH $S{CMAKE_CURRENT_LIST_DIR}/reactor-uc) |set(LFC_GEN_INCLUDE_DIRS $S{CMAKE_CURRENT_LIST_DIR}) |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") |set(EVENT_QUEUE_SIZE ${max(numEvents, 1)} CACHE STRING "Size of the event queue") - | """.trimMargin() } - fun generateCmakePosix(sources: List) = with(PrependOperator) { + protected fun generateCmakePosix(sources: List, compileDefs: List) = with(PrependOperator) { """ - |cmake_minimum_required(VERSION 3.10) + |cmake_minimum_required(VERSION $minCmakeVersion) |project(${mainTarget} LANGUAGES C) - |set(PLATFORM POSIX CACHE STRING "Target platform") - |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") - |set(EVENT_QUEUE_SIZE ${max(numEvents, 1)} CACHE STRING "Size of the event queue") - |set(CMAKE_BUILD_TYPE ${targetConfig.getOrDefault(BuildTypeProperty.INSTANCE)}) - | |set(LF_MAIN_TARGET ${mainTarget}) - |set(SOURCES - ${" | "..sources.joinWithLn { it.toUnixString() }} - |) - |add_executable($S{LF_MAIN_TARGET} $S{SOURCES}) + |set(CMAKE_BUILD_TYPE ${targetConfig.getOrDefault(BuildTypeProperty.INSTANCE)}) + |set(PLATFORM POSIX CACHE STRING "Target platform") + ${" |"..generateCmakeCommon(sources, compileDefs)} + |add_executable($S{LF_MAIN_TARGET} $S{LFC_GEN_SOURCES} $S{LFC_GEN_MAIN}) |install(TARGETS $S{LF_MAIN_TARGET} | RUNTIME DESTINATION $S{CMAKE_INSTALL_BINDIR} | OPTIONAL |) - |add_compile_definitions(LF_LOG_LEVEL_ALL=${targetConfig.getOrDefault(LoggingProperty.INSTANCE).ordinal}) - | - |add_subdirectory(reactor-uc $S{CMAKE_CURRENT_LIST_DIR}) + |add_compile_definitions("LF_LOG_LEVEL_ALL=LF_LOG_LEVEL_${targetConfig.getOrDefault(LoggingProperty.INSTANCE).name.uppercase()}") + |add_compile_definitions($S{LFC_GEN_COMPILE_DEFS}) + |add_subdirectory($S{REACTOR_UC_PATH}) |target_link_libraries($S{LF_MAIN_TARGET} PRIVATE reactor-uc) - |target_include_directories($S{LF_MAIN_TARGET} PRIVATE $S{CMAKE_CURRENT_LIST_DIR}) - ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")} + |target_include_directories($S{LF_MAIN_TARGET} PRIVATE $S{LFC_GEN_INCLUDE_DIRS}) + ${" |"..(includeFiles?.joinWithLn { "include(\"$it\")" } ?: "")} """.trimMargin() } } + +class UcCmakeGeneratorNonFederated(private val mainDef: Instantiation, targetConfig: TargetConfig, fileConfig: UcFileConfig, numEvents: Int, numReactions: Int): UcCmakeGenerator(targetConfig, fileConfig, numEvents, numReactions) { + override val mainTarget = fileConfig.name + + override fun generateCmake(sources: List) = + if (platform == PlatformType.Platform.NATIVE) { + generateCmakePosix(sources, emptyList()) + } else { + generateCmakeCommon(sources, emptyList()) + } +} + +class UcCmakeGeneratorFederated(private val federate: UcFederate, targetConfig: TargetConfig, fileConfig: UcFileConfig, numEvents: Int, numReactions: Int): UcCmakeGenerator(targetConfig, fileConfig, numEvents, numReactions) { + override val mainTarget = federate.codeType + + override fun generateCmake(sources: List) = + if (platform == PlatformType.Platform.NATIVE) { + generateCmakePosix(sources, federate.getCompileDefs()) + } else { + generateCmakeCommon(sources, federate.getCompileDefs()) + } +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 012f7723..244582ec 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -7,16 +7,79 @@ import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate import org.lflang.generator.uc.UcReactorGenerator.Companion.codeType import org.lflang.lf.* +/** + * This generator creates code for configuring the connections between reactors. This is perhaps the most complicated + * part of the code-generator. This generator handles both federated and non-federated programs + */ class UcConnectionGenerator( - private val reactor: Reactor, - private val currentFederate: UcFederate?, - private val allFederates: List + private val reactor: Reactor, // The reactor to generator connections for + private val currentFederate: UcFederate?, // The federate to generate connections for. If set then `reactor` should be the top-level reactor. + private val allFederates: List // A list of all the federates in the program. Only used for federated code-gen. ) { + /** A list containing all non-federated gruoped connections within this reactor. */ private val nonFederatedConnections: List + + /** A list containing all federated connection bundles (each containing grouped connections) within this reactor, that + * has the current federate as a src or dest. + */ private val federatedConnectionBundles: List + private val isFederated = currentFederate != null + /** + * Given a LF connection and possibly the list of federates of the program. Create all the ConnectionChannels + * found within the LF Connection. This must handle multiports, banks, iterated connections and federated connections. + */ + private fun parseConnectionChannels(conn: Connection, federates: List): List { + val res = mutableListOf() + val rhsPorts = conn.rightPorts.map { getChannelQueue(it, federates) } + var rhsPortIndex = 0 + + var lhsPorts = conn.leftPorts.map { getChannelQueue(it, federates) } + var lhsPortIndex = 0 + + // Keep parsing out connections until we are out of right-hand-side (rhs) ports + while (rhsPortIndex < rhsPorts.size) { + // First get the current lhs and rhs port and UcGroupedConnection that we are working with + val lhsPort = lhsPorts[lhsPortIndex] + val rhsPort = rhsPorts[rhsPortIndex] + if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { + val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) + val lhsChannelsToAdd = lhsPort.takeRemainingChannels() + lhsChannelsToAdd.zip(rhsChannelsToAdd) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } + lhsPortIndex += 1 + } else if (rhsPort.channelsLeft() < lhsPort.channelsLeft()) { + val numRhsChannelsToAdd = rhsPort.channelsLeft() + val rhsChannelsToAdd = rhsPort.takeRemainingChannels() + val lhsChannelsToAdd = lhsPort.takeChannels(numRhsChannelsToAdd) + lhsChannelsToAdd.zip(rhsChannelsToAdd) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } + rhsPortIndex += 1 + } else { + lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()) + .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } + rhsPortIndex += 1 + lhsPortIndex += 1 + } + + // If we are out of lhs variables, but not rhs, then there should be an iterated connection. + // We handle it by resetting the lhsChannels variable and index and continuing until + // we have been through all rhs channels. + if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { + assert(conn.isIterated) + lhsPorts = conn.leftPorts.map {getChannelQueue(it, federates)} + lhsPortIndex = 0 + } + } + return res + } + + /** + * Given a list of ConnectionChannels, group them together. How they are grouepd depends on + * whether we are dealing with federated or non-federated reactors. + */ private fun groupConnections(channels: List): List { val res = mutableListOf() val channels = HashSet(channels) @@ -65,68 +128,27 @@ class UcConnectionGenerator( return res } - private fun getUcPorts(portVarRefs: List, federates: List): List { - return portVarRefs.map { c -> - if (c.container?.isAFederate ?: false) { - val federates = allFederates.filter { it.inst == c.container } - UcPort(c, federates) + /** Given a port VarRef, and the list of federates. Create a channel queue. I.e. create + * all the UcChannels and associate them with the correct federates. + */ + private fun getChannelQueue(portVarRef: VarRef, federates: List): UcChannelQueue { + return if (portVarRef.container?.isAFederate ?: false) { + val federates = allFederates.filter { it.inst == portVarRef.container } + UcChannelQueue(portVarRef, federates) } else { - UcPort(c, emptyList()) + UcChannelQueue(portVarRef, emptyList()) } - } } - private fun parseConnectionChannels(conn: Connection, federates: List): List { - val res = mutableListOf() - val rhsPorts = getUcPorts(conn.rightPorts, federates) - var rhsPortIndex = 0 - - var lhsPorts = getUcPorts(conn.leftPorts, federates) - var lhsPortIndex = 0 - - // Keep parsing out connections until we are out of right-hand-side (rhs) ports - while (rhsPortIndex < rhsPorts.size) { - // First get the current lhs and rhs port and UcGroupedConnection that we are working with - val lhsPort = lhsPorts[lhsPortIndex] - val rhsPort = rhsPorts[rhsPortIndex] - if (rhsPort.channelsLeft() > lhsPort.channelsLeft()) { - val rhsChannelsToAdd = rhsPort.takeChannels(lhsPort.channelsLeft()) - val lhsChannelsToAdd = lhsPort.takeRemainingChannels() - lhsChannelsToAdd.zip(rhsChannelsToAdd) - .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } - lhsPortIndex += 1 - } else if (rhsPort.channelsLeft() < lhsPort.channelsLeft()) { - val numRhsChannelsToAdd = rhsPort.channelsLeft() - val rhsChannelsToAdd = rhsPort.takeRemainingChannels() - val lhsChannelsToAdd = lhsPort.takeChannels(numRhsChannelsToAdd) - lhsChannelsToAdd.zip(rhsChannelsToAdd) - .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } - rhsPortIndex += 1 - } else { - lhsPort.takeRemainingChannels().zip(rhsPort.takeRemainingChannels()) - .forEach { res.add(UcConnectionChannel(it.first, it.second, conn)) } - rhsPortIndex += 1 - lhsPortIndex += 1 - } - - // If we are out of lhs variables, but not rhs, then there should be an iterated connection. - // We handle it by resetting the lhsChannels variable and index and continuing until - // we have been through all rhs channels. - if (lhsPortIndex >= lhsPorts.size && rhsPortIndex < rhsPorts.size) { - assert(conn.isIterated) - lhsPorts = getUcPorts(conn.leftPorts, federates) - lhsPortIndex = 0 - } - } - return res - } companion object { private val Connection.delayString get(): String = this.delay.orNever().toCCode() + /** Whether we have initialized the UcFederates with NetworkInterfaces. This is only done once. */ private var federateInterfacesInitialized = false + /** A global list of FederatedConnectionBundles. It is computed once and reused when code-generating */ private var allFederatedConnectionBundles: List = emptyList() private fun createFederatedConnectionBundles(groupedConnections: List) { diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt index 7e180af2..9688358b 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionUtils.kt @@ -9,10 +9,13 @@ import org.lflang.lf.Connection import org.lflang.lf.Port import org.lflang.lf.VarRef - +/** A UcConnectionChannel is the fundamental lowest-level representation of a connection in a LF program. + * It connects two UcChannels, one at the source and one at the destination. + */ class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Connection) { val isFederated = (src.federate != null) && (dest.federate != null) && (src.federate != dest.federate) + /** Get the NetworkChannelType of this connection. If we are not in a federated program it is NONE */ fun getChannelType(): NetworkChannelType { val linkAttr = getLinkAttribute(conn) return if (linkAttr == null) { @@ -28,6 +31,12 @@ class UcConnectionChannel(val src: UcChannel, val dest: UcChannel, val conn: Con } } +/** + * A GroupedConnection is a set of ConnectionChannels that can be grouped together for efficiency. + * All ConnectionChannels that start from the same LF port, either because of multiports, banks, or + * multiple connections. Are grouped. + * TODO: Give a better exaplanation for what a GroupedConnection is. + */ open class UcGroupedConnection( val src: VarRef, val channels: List, @@ -58,6 +67,13 @@ open class UcGroupedConnection( fun getUniqueName() = "conn_${srcInst?.name ?: ""}_${srcPort.name}_${uid}" } +/** + * In federated programs, we must group connections differently. For a non federated program. + * A single output port connected to N different input ports could be grouped to a single GroupedConnection. + * For a federated program, we would need N GroupedConnections if an output port goes to N different federates. + * Moreover, if there are several kinds of NetworkChannels in the program, then we can only group connections + * transported over the same channels. + */ class UcFederatedGroupedConnection( src: VarRef, channels: List, @@ -66,16 +82,21 @@ class UcFederatedGroupedConnection( val destFed: UcFederate, ) : UcGroupedConnection(src, channels, lfConn) { + // FIXME: Allow user to override and provide these. val serializeFunc = "serialize_payload_default" val deserializeFunc = "deserialize_payload_default" } +/** + * A FederatedConnectionBundle will contain all GroupedConnections going between two federates, in either direction. + * It also contains a NetworkChannel connecting and NetworkEndpoint in each federate. + */ class UcFederatedConnectionBundle( val src: UcFederate, val dest: UcFederate, val groupedConnections: List ) { - val networkChannel: UcNetworkChannel = UcNetworkChannel.createNetworkChannelForBundle(this) + val networkChannel: UcNetworkChannel = UcNetworkChannel.createNetworkEndpointsAndChannelForBundle(this) fun numOutputs(federate: UcFederate) = groupedConnections.count { it.srcFed == federate } @@ -89,8 +110,9 @@ class UcFederatedConnectionBundle( } } -// Convenience class around a port variable reference. It is used to encapsulate the management of multi-connections -// where a single lhs port has to +/** + * An UcChannel represents a single channel of an LF Port. Due to Multiports and Banks, each LF Port can have multiple channels. + */ class UcChannel(val varRef: VarRef, val portIdx: Int, val bankIdx: Int, val federate: UcFederate?) { fun getCodePortIdx() = portIdx fun getCodeBankIdx() = if (federate == null) bankIdx else 0 @@ -103,13 +125,18 @@ class UcChannel(val varRef: VarRef, val portIdx: Int, val bankIdx: Int, val fede fun generateChannelPointer() = "&self->${reactorInstance}${portInstance}" } -// Wrapper around a variable reference to a Port. Contains a channel for each bank/multiport within it. -// For each connection statement where a port is referenced, we create an UcPort and use this class -// to figure out how the individual channels are connect to other UcPorts. -class UcPort(varRef: VarRef, federates: List) { +/** + * This is a convenience-wrapper around a LF Port. It will construct + * UcChannels corresponding to the multiports and banks. + * + * If this is a federates program. it must be passed a list of federates associated with the LF Port. + * It is a list in case it is a bank. Then each federate of the bank must be passed to the constructor. + */ +class UcChannelQueue(varRef: VarRef, federates: List) { private val bankWidth = varRef.container?.width ?: 1 private val portWidth = (varRef.variable as Port).width private val isInterleaved = varRef.isInterleaved + /** A queue of UcChannels that can be popped of as we create UcConnetions */ private val channels = ArrayDeque() // Construct the stack of channels belonging to this port. If this port is interleaved, diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt index 02416617..519f6146 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederateGenerator.kt @@ -15,9 +15,6 @@ class UcFederateGenerator(private val currentFederate: UcFederate, private val o private val reactions = UcReactionGenerator(container) private val instances = UcInstanceGenerator(container, parameters, ports, connections, reactions, fileConfig, messageReporter) private val headerFile = "lf_federate.h" - - fun numBundles() = connections.getNumFederatedConnectionBundles() - private val includeGuard = "LFC_GEN_FEDERATE_${currentFederate.inst.name.uppercase()}_H" fun getMaxNumPendingEvents(): Int { @@ -31,7 +28,7 @@ class UcFederateGenerator(private val currentFederate: UcFederate, private val o ${" | "..instances.generateReactorStructField(currentFederate.inst)} ${" | "..connections.generateReactorStructFields()} ${" | "..connections.generateFederateStructFields()} - | LF_FEDERATE_BOOKKEEPING_INSTANCES(${numBundles()}); + | LF_FEDERATE_BOOKKEEPING_INSTANCES(${connections.getNumFederatedConnectionBundles()}); |} ${currentFederate.codeType}; | """.trimMargin() diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt index 4b55df34..2709d7c0 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedLaunchScriptGenerator.kt @@ -1,11 +1,7 @@ package org.lflang.generator.uc import org.lflang.generator.PrependOperator -import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate import org.lflang.joinWithLn -import org.lflang.lf.Instantiation -import org.lflang.toUnixString -import kotlin.io.path.name class UcFederatedLaunchScriptGenerator(private val fileConfig: UcFileConfig) { private val S = '$' // a little trick to escape the dollar sign with $S @@ -40,15 +36,15 @@ class UcFederatedLaunchScriptGenerator(private val fileConfig: UcFileConfig) { """.trimMargin() } - fun launchFederate(federate: UcFederate) = with(PrependOperator) { - """ |echo "#### Launching federate ${federate.codeType}" - |if [ "${S}1" = "-l" ]; then - | ${fileConfig.binPath}/${federate.codeType} | tee ${federate.codeType}.log & - |else - | ${fileConfig.binPath}/${federate.codeType} & - |fi - |pids+=($S!) - | - """.trimMargin() - } + private fun launchFederate(federate: UcFederate) = + """ + |echo "#### Launching federate ${federate.codeType}" + |if [ "${S}1" = "-l" ]; then + | ${fileConfig.binPath}/${federate.codeType} | tee ${federate.codeType}.log & + |else + | ${fileConfig.binPath}/${federate.codeType} & + |fi + |pids+=($S!) + | + """.trimMargin() } \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt deleted file mode 100644 index 885c13c5..00000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedMainGenerator.kt +++ /dev/null @@ -1,74 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.target.TargetConfig -import org.lflang.generator.PrependOperator -import org.lflang.lf.Reactor -import org.lflang.reactor -import org.lflang.target.property.FastProperty -import org.lflang.target.property.KeepaliveProperty -import org.lflang.target.property.TimeOutProperty - -class UcFederatedMainGenerator( - private val currentFederate: UcFederate, - private val otherFederates: List, - private val targetConfig: TargetConfig, - private val fileConfig: UcFileConfig, -) { - - private val top = currentFederate.inst.eContainer() as Reactor - private val ucConnectionGenerator = UcConnectionGenerator(top, currentFederate, otherFederates) - - fun getDuration() = if (targetConfig.isSet(TimeOutProperty.INSTANCE)) targetConfig.get(TimeOutProperty.INSTANCE).toCCode() else "FOREVER" - - fun keepAlive() = if(targetConfig.isSet(KeepaliveProperty.INSTANCE)) "true" else "false" - - fun fast() = if(targetConfig.isSet(FastProperty.INSTANCE)) "true" else "false" - - fun generateStartSource() = with(PrependOperator) { - """ - |#include "reactor-uc/reactor-uc.h" - |#include "lf_federate.h" - |static ${currentFederate.codeType} main_reactor; - |static Environment lf_environment; - |Environment *_lf_environment = &lf_environment; - |void lf_exit(void) { - | Environment_free(&lf_environment); - |} - |void lf_start(void) { - | Environment_ctor(&lf_environment, (Reactor *)&main_reactor); - | lf_environment.scheduler->duration = ${getDuration()}; - | lf_environment.scheduler->keep_alive = ${keepAlive()}; - | lf_environment.scheduler->leader = ${top.instantiations.first() == currentFederate.inst && currentFederate.bankIdx == 0}; - | lf_environment.fast_mode = ${fast()}; - | lf_environment.has_async_events = ${currentFederate.inst.reactor.inputs.isNotEmpty()}; - | ${currentFederate.codeType}_ctor(&main_reactor, NULL, &lf_environment); - | lf_environment.net_bundles_size = ${ucConnectionGenerator.getNumFederatedConnectionBundles()}; - | lf_environment.net_bundles = (FederatedConnectionBundle **) &main_reactor._bundles; - | lf_environment.assemble(&lf_environment); - | lf_environment.start(&lf_environment); - | lf_exit(); - |} - """.trimMargin() - } - - fun generateStartHeader() = with(PrependOperator) { - """ - |#ifndef REACTOR_UC_LF_MAIN_H - |#define REACTOR_UC_LF_MAIN_H - | - |void lf_start(void); - | - |#endif - | - """.trimMargin() - } - - fun generateMainSource() = with(PrependOperator) { - """ - |#include "lf_start.h" - |int main(void) { - | lf_start(); - |} - """.trimMargin() - } -} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt deleted file mode 100644 index 3b17aade..00000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFederatedPlatformGenerator.kt +++ /dev/null @@ -1,207 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.generator.CodeMap -import org.lflang.generator.LFGeneratorContext -import org.lflang.generator.uc.UcInstanceGenerator.Companion.codeTypeFederate -import org.lflang.reactor -import org.lflang.target.property.BuildTypeProperty -import org.lflang.target.property.type.BuildTypeType.BuildType -import org.lflang.toUnixString -import org.lflang.util.FileUtil -import org.lflang.util.LFCommand -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import kotlin.io.path.createSymbolicLinkPointingTo - -class UcFederatedPlatformGenerator(generator: UcFederatedGenerator, private val srcGenPath: Path, private val federate: UcFederate) : - UcPlatformGenerator(generator) { - - companion object { - fun buildTypeToCmakeConfig(type: BuildType) = when (type) { - BuildType.TEST -> "Debug" - else -> type.toString() - } - } - - val buildPath = srcGenPath.resolve("build") - - override fun generatePlatformFiles() { - val generator = generator as UcFederatedGenerator - val reactorUCEnvPath = System.getenv("REACTOR_UC_PATH") - if (reactorUCEnvPath == null) { - messageReporter.nowhere().error("REACTOR_UC_PATH environment variable not defined. Do source env.bash in reactor-uc") - return; - } - val runtimePath: Path = Paths.get(reactorUCEnvPath) - val mainGenerator = UcFederatedMainGenerator(federate, generator.federates, generator.targetConfig, generator.fileConfig) - - val startSourceFile = Paths.get("lf_start.c") - val startHeaderFile = Paths.get("lf_start.h") - val mainSourceFile = Paths.get("lf_main.c") - - val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateStartSource()) - val mainCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateMainSource()) - - ucSources.addAll(listOf(startSourceFile, mainSourceFile)) - codeMaps[srcGenPath.resolve(startSourceFile)] = startCodeMap - codeMaps[srcGenPath.resolve(mainSourceFile)] = mainCodeMap - - FileUtil.writeToFile(startCodeMap.generatedCode, srcGenPath.resolve(startSourceFile), true) - FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) - FileUtil.writeToFile(mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) - - val numEventsAndReactions = generator.totalNumEventsAndReactionsFederated(federate) - - val cmakeGenerator = UcCmakeFederatedGenerator(federate, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) - val makeGenerator = UcMakeGenerator(federate.inst.reactor, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) - FileUtil.writeToFile(cmakeGenerator.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) - - val launchScriptGenerator = UcFederatedLaunchScriptGenerator(fileConfig) - FileUtil.writeToFile(launchScriptGenerator.generateLaunchScript(generator.federates), fileConfig.binPath.resolve(fileConfig.name)) - fileConfig.binPath.resolve(fileConfig.name).toFile().setExecutable(true) - - val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); - try { - runtimeSymlinkPath.createSymbolicLinkPointingTo(runtimePath); - } catch (e: Exception) { - // Do nothing - } - - FileUtil.writeToFile(makeGenerator.generateMake(ucSources), srcGenPath.resolve("Makefile"), true) - } - - override fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean): Boolean { - - // make sure the build directory exists - Files.createDirectories(buildPath) - - val version = checkCmakeVersion() - var parallelize = true - if (version != null && version.compareVersion("3.12.0") < 0) { - messageReporter.nowhere().warning("CMAKE is older than version 3.12. Parallel building is not supported.") - parallelize = false - } - - if (version != null) { - val cmakeReturnCode = runCmake(context) - - if (cmakeReturnCode == 0 && !onlyGenerateBuildFiles) { - // If cmake succeeded, run make - val makeCommand = createMakeCommand(buildPath, parallelize, federate.codeType) - val makeReturnCode = UcValidator(fileConfig, messageReporter, codeMaps).run(makeCommand, context.cancelIndicator) - var installReturnCode = 0 - if (makeReturnCode == 0) { - val installCommand = createMakeCommand(buildPath, parallelize, "install") - installReturnCode = installCommand.run(context.cancelIndicator) - if (installReturnCode == 0) { - println("SUCCESS (compiling generated C code)") - println("Generated source code is in ${fileConfig.srcGenPath}") - println("Compiled binary is in ${fileConfig.binPath}") - } - } - if ((makeReturnCode != 0 || installReturnCode != 0) && !messageReporter.errorsOccurred) { - // If errors occurred but none were reported, then the following message is the best we can do. - messageReporter.nowhere().error("make failed with error code $makeReturnCode") - } - } - if (cmakeReturnCode != 0) { - messageReporter.nowhere().error("cmake failed with error code $cmakeReturnCode") - } - } - return !messageReporter.errorsOccurred - } - - - private fun checkCmakeVersion(): String? { - // get the installed cmake version and make sure it is at least 3.5 - val cmd = commandFactory.createCommand("cmake", listOf("--version"), buildPath) - var version: String? = null - if (cmd != null && cmd.run() == 0) { - val regex = "\\d+(\\.\\d+)+".toRegex() - version = regex.find(cmd.output.toString())?.value - } - if (version == null || version.compareVersion("3.5.0") < 0) { - messageReporter.nowhere( - ).error( - "The uC target requires CMAKE >= 3.5.0 to compile the generated code. " + - "Auto-compiling can be disabled using the \"no-compile: true\" target property." - ) - return null - } - - return version - } - - - /** - * Run CMake to generate build files. - * @return True, if cmake run successfully - */ - private fun runCmake(context: LFGeneratorContext): Int { - val cmakeCommand = createCmakeCommand(buildPath, fileConfig.outPath) - return cmakeCommand.run(context.cancelIndicator) - } - - private fun String.compareVersion(other: String): Int { - val a = this.split(".").map { it.toInt() } - val b = other.split(".").map { it.toInt() } - for (x in (a zip b)) { - val res = x.first.compareTo(x.second) - if (res != 0) - return res - } - return 0 - } - - private fun getMakeArgs(buildPath: Path, parallelize: Boolean, target: String): List { - val cmakeConfig = buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE)) - val makeArgs = mutableListOf( - "--build", - buildPath.fileName.toString(), - "--config", - cmakeConfig, - "--target", - target - ) - - if (parallelize) { - makeArgs.addAll(listOf("--parallel", Runtime.getRuntime().availableProcessors().toString())) - } - - return makeArgs - } - - - private fun createMakeCommand(buildPath: Path, parallelize: Boolean, target: String): LFCommand { - val makeArgs = getMakeArgs(buildPath, parallelize, target) - return commandFactory.createCommand("cmake", makeArgs, buildPath.parent) - } - - private fun getCmakeArgs( - buildPath: Path, - outPath: Path, - sourcesRoot: String? = null - ) = cmakeArgs + listOf( - "-DCMAKE_INSTALL_PREFIX=${outPath.toUnixString()}", - "-DCMAKE_INSTALL_BINDIR=$relativeBinDir", - "--fresh", - "-S", - sourcesRoot ?: srcGenPath.toUnixString(), - "-B", - buildPath.toString() - ) - - private fun createCmakeCommand( - buildPath: Path, - outPath: Path, - sourcesRoot: String? = null - ): LFCommand { - val cmd = commandFactory.createCommand( - "cmake", - getCmakeArgs(buildPath, outPath, sourcesRoot), - buildPath.parent - ) - return cmd - } -} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt index 09e1347c..b8ea6b4c 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcFileConfig.kt @@ -34,7 +34,4 @@ class UcFileConfig(resource: Resource, srcGenBasePath: Path, useHierarchicalBin: /** Path to the header file corresponding to this reactor */ fun getReactorHeaderPath(r: Reactor): Path = getGenDir(r.eResource()).resolve("${r.name}.h") - - /** Path to the build directory containing CMake-generated files */ - val buildPath: Path get() = this.outPath.resolve("build") } \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt index 14fc7e4a..c306d128 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGenerator.kt @@ -7,29 +7,25 @@ import org.eclipse.xtext.xbase.lib.IteratorExtensions import org.lflang.allInstantiations import org.lflang.allReactions import org.lflang.generator.* -import org.lflang.generator.GeneratorUtils.canGenerate import org.lflang.generator.uc.UcInstanceGenerator.Companion.width import org.lflang.generator.uc.UcReactorGenerator.Companion.hasStartup -import org.lflang.isGeneric import org.lflang.lf.Instantiation -import org.lflang.lf.LfFactory import org.lflang.lf.Reactor import org.lflang.reactor import org.lflang.scoping.LFGlobalScopeProvider import org.lflang.target.Target import org.lflang.target.property.* -import org.lflang.target.property.type.PlatformType -import org.lflang.util.FileUtil import java.nio.file.Path +/** Creates either a Federated or NonFederated generator depending on the type of LF program */ fun createUcGenerator(context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider): UcGenerator { val nodes: Iterable = IteratorExtensions.toIterable(context.getFileConfig().resource.getAllContents()); for (reactor in nodes.filterIsInstance()) { if (reactor.isFederated) { - return UcFederatedGenerator(context, scopeProvider) + return UcGeneratorFederated(context, scopeProvider) } } - return UcNonFederatedGenerator(context, scopeProvider) + return UcGeneratorNonFederated(context, scopeProvider) } @Suppress("unused") @@ -44,10 +40,13 @@ abstract class UcGenerator( val fileConfig: UcFileConfig = context.fileConfig as UcFileConfig val platform = targetConfig.get(PlatformProperty.INSTANCE) + // Contains the maximum number of pending events required by each reactor. + // Is updated as reactors are analyzed and code-generated. val maxNumPendingEvents = mutableMapOf() - - private fun _totalNumEventsAndReactions(inst: Instantiation): Triple { + // Compute the total number of events and reactions within an instance (and its children) + // Also returns whether there is any startup event within the instance. + private fun totalNumEventsAndReactions(inst: Instantiation): Triple { var numEvents = 0 var numReactions = 0 var hasStartup = false @@ -55,7 +54,7 @@ abstract class UcGenerator( remaining.addAll(inst.reactor.allInstantiations) while (remaining.isNotEmpty()) { val child = remaining.removeFirst() - val childRes = _totalNumEventsAndReactions(child) + val childRes = totalNumEventsAndReactions(child) numEvents += childRes.first * child.width numReactions += childRes.second * child.width @@ -67,11 +66,12 @@ abstract class UcGenerator( return Triple(numEvents, numReactions, hasStartup) } + // Compute the total number of events and reactions for a top-level reactor. fun totalNumEventsAndReactions(main: Reactor): Pair { val res = MutablePair(maxNumPendingEvents[main]!!, main.allReactions.size) var hasStartup = main.hasStartup for (inst in main.allInstantiations) { - val childRes = _totalNumEventsAndReactions(inst) + val childRes = totalNumEventsAndReactions(inst) res.left += childRes.first * inst.width res.right += childRes.second * inst.width hasStartup = hasStartup or childRes.third @@ -86,9 +86,8 @@ abstract class UcGenerator( const val MINIMUM_CMAKE_VERSION = "3.5" } - // Returns a possibly empty list of the federates in the current program - - fun getAllFederates(): List { + // Returns a possibly empty list of the federates in the current program. + protected fun getAllFederates(): List { val res = mutableListOf() for (reactor in reactors) { if (reactor.isFederated) { @@ -98,8 +97,8 @@ abstract class UcGenerator( return res } - - fun getAllInstantiatedReactors(top: Reactor): List { + // Returns a list of all instantiated reactors within a top-level reactor. + protected fun getAllInstantiatedReactors(top: Reactor): List { val res = mutableListOf() for (inst in top.allInstantiations) { res.add(inst.reactor) @@ -107,69 +106,8 @@ abstract class UcGenerator( } return res.distinct() } -} - -class UcNonFederatedGenerator( - context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider -) : UcGenerator(context, scopeProvider) { - - - fun doGenerateReactor( - resource: Resource, - context: LFGeneratorContext, - srcGenPath: Path, - ): GeneratorResult.Status { - if (!canGenerate(errorsOccurred(), mainDef, messageReporter, context)) return GeneratorResult.Status.FAILED - - // generate header and source files for all reactors - getAllInstantiatedReactors(mainDef.reactor).map { generateReactorFiles(it, srcGenPath) } - - generateReactorFiles(mainDef.reactor, srcGenPath) - - for (r in getAllImportedResources(resource)) { - val generator = UcPreambleGenerator(r, fileConfig, scopeProvider) - val headerFile = fileConfig.getPreambleHeaderPath(r); - val preambleCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) - codeMaps[srcGenPath.resolve(headerFile)] = preambleCodeMap - FileUtil.writeToFile(preambleCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) - } - return GeneratorResult.Status.GENERATED - } - - - override fun doGenerate(resource: Resource, context: LFGeneratorContext) { - super.doGenerate(resource, context) - - if (getAllFederates().isNotEmpty()) { - context.errorReporter.nowhere().error("Federated program detected in non-federated generator") - return - } - - val res = doGenerateReactor( - resource, - context, - fileConfig.srcGenPath, - ) - if (res == GeneratorResult.Status.GENERATED) { - // generate platform specific files - val platformGenerator = UcStandalonePlatformGenerator(this, fileConfig.srcGenPath) - platformGenerator.generatePlatformFiles() - if (platform.platform == PlatformType.Platform.NATIVE) { - if (platformGenerator.doCompile(context)) { - context.finish(GeneratorResult.Status.COMPILED, codeMaps) - } else { - context.finish(GeneratorResult.Status.FAILED, codeMaps) - } - } else { - context.finish(GeneratorResult.GENERATED_NO_EXECUTABLE.apply(context, codeMaps)) - } - } else { - context.finish(GeneratorResult.Status.FAILED, codeMaps) - } - } - - private fun getAllImportedResources(resource: Resource): Set { + protected fun getAllImportedResources(resource: Resource): Set { val resources: MutableSet = scopeProvider.getImportedResources(resource) val importedRresources = resources.subtract(setOf(resource)) resources.addAll(importedRresources.map { getAllImportedResources(it) }.flatten()) @@ -177,184 +115,6 @@ class UcNonFederatedGenerator( return resources } - fun generateReactorFiles(reactor: Reactor, srcGenPath: Path) { - val generator = UcReactorGenerator(reactor, fileConfig, messageReporter) - maxNumPendingEvents.set(reactor, generator.getMaxNumPendingEvents()) - - val headerFile = fileConfig.getReactorHeaderPath(reactor) - val sourceFile = fileConfig.getReactorSourcePath(reactor) - val reactorCodeMap = CodeMap.fromGeneratedCode(generator.generateSource()) - if (!reactor.isGeneric) ucSources.add(sourceFile) - codeMaps[srcGenPath.resolve(sourceFile)] = reactorCodeMap - val headerCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) - codeMaps[srcGenPath.resolve(headerFile)] = headerCodeMap - - FileUtil.writeToFile(headerCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) - FileUtil.writeToFile(reactorCodeMap.generatedCode, srcGenPath.resolve(sourceFile), true) - } - override fun getTarget() = Target.UC override fun getTargetTypes(): TargetTypes = UcTypes } - -class UcFederatedGenerator( - context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider -) : UcGenerator(context, scopeProvider) { - - private val nonFederatedGenerator = UcNonFederatedGenerator(context, scopeProvider) - val federates = mutableListOf() - - fun totalNumEventsAndReactionsFederated(federate: UcFederate): Pair { - val eventsFromFederatedConncetions = maxNumPendingEvents[mainDef.reactor]!! - val eventsAndReactionsInFederate = nonFederatedGenerator.totalNumEventsAndReactions(federate.inst.reactor) - return Pair( - eventsFromFederatedConncetions + eventsAndReactionsInFederate.first, - eventsAndReactionsInFederate.second - ) - } - - fun doGenerateFederate( - resource: Resource, - context: LFGeneratorContext, - srcGenPath: Path, - federate: UcFederate - ): GeneratorResult.Status { - if (!canGenerate( - errorsOccurred(), - federate.inst, - messageReporter, - context - ) - ) return GeneratorResult.Status.FAILED - - // generate all core files - generateFiles(federate, srcGenPath, getAllImportedResources(resource)) - return GeneratorResult.Status.GENERATED - } - - private fun clearStateFromPreviousFederate() { - codeMaps.clear() - ucSources.clear() - maxNumPendingEvents.clear() - nonFederatedGenerator.maxNumPendingEvents.clear() - nonFederatedGenerator.codeMaps.clear() - nonFederatedGenerator.ucSources.clear() - } - - private fun createMainDef() { - for (reactor in reactors) { - if (reactor.isFederated) { - this.mainDef = LfFactory.eINSTANCE.createInstantiation() - this.mainDef.name = reactor.name - this.mainDef.reactorClass = reactor - } - } - } - - override fun doGenerate(resource: Resource, context: LFGeneratorContext) { - super.doGenerate(resource, context) - createMainDef() - for (inst in getAllFederates()) { - for (bankIdx in 0.. { - val resources: MutableSet = scopeProvider.getImportedResources(resource) - val importedRresources = resources.subtract(setOf(resource)) - resources.addAll(importedRresources.map { getAllImportedResources(it) }.flatten()) - resources.add(resource) - return resources - } - - private fun generateFederateFiles(federate: UcFederate, srcGenPath: Path) { - // First thing is that we need to also generate the top-level federate reactor files - nonFederatedGenerator.generateReactorFiles(federate.inst.reactor, srcGenPath) - - // Then we generate a reactor which wraps around the top-level reactor in the federate. - val generator = UcFederateGenerator(federate, federates, fileConfig, messageReporter) - val top = federate.inst.eContainer() as Reactor - - // Record the number of events and reactions in this reactor - maxNumPendingEvents.set(top, generator.getMaxNumPendingEvents()) - - val headerFile = srcGenPath.resolve("lf_federate.h") - val sourceFile = srcGenPath.resolve("lf_federate.c") - val federateCodeMap = CodeMap.fromGeneratedCode(generator.generateSource()) - ucSources.add(sourceFile) - codeMaps[srcGenPath.resolve(sourceFile)] = federateCodeMap - val headerCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) - codeMaps[srcGenPath.resolve(headerFile)] = headerCodeMap - - FileUtil.writeToFile(headerCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) - FileUtil.writeToFile(federateCodeMap.generatedCode, srcGenPath.resolve(sourceFile), true) - } - - private fun generateFiles(federate: UcFederate, srcGenPath: Path, resources: Set) { - // generate header and source files for all reactors - getAllInstantiatedReactors(federate.inst.reactor).map { - nonFederatedGenerator.generateReactorFiles( - it, - srcGenPath - ) - } - - generateFederateFiles(federate, srcGenPath) - - // Collect the info on the generated sources - ucSources.addAll(nonFederatedGenerator.ucSources) - codeMaps.putAll(nonFederatedGenerator.codeMaps) - - for (r in resources) { - val generator = UcPreambleGenerator(r, fileConfig, scopeProvider) - val headerFile = fileConfig.getPreambleHeaderPath(r); - val preambleCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) - codeMaps[srcGenPath.resolve(headerFile)] = preambleCodeMap - FileUtil.writeToFile(preambleCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) - } - } - - override fun getTarget() = Target.UC - - override fun getTargetTypes(): TargetTypes = UcTypes -} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt new file mode 100644 index 00000000..ab72af76 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcGeneratorFederated.kt @@ -0,0 +1,160 @@ +package org.lflang.generator.uc + +import org.eclipse.emf.ecore.resource.Resource +import org.lflang.generator.CodeMap +import org.lflang.generator.GeneratorResult +import org.lflang.generator.GeneratorUtils.canGenerate +import org.lflang.generator.LFGeneratorContext +import org.lflang.generator.uc.UcInstanceGenerator.Companion.width +import org.lflang.lf.LfFactory +import org.lflang.lf.Reactor +import org.lflang.reactor +import org.lflang.scoping.LFGlobalScopeProvider +import org.lflang.target.property.type.PlatformType +import org.lflang.util.FileUtil +import java.nio.file.Path +import java.nio.file.Paths + +class UcGeneratorFederated( + context: LFGeneratorContext, scopeProvider: LFGlobalScopeProvider +) : UcGenerator(context, scopeProvider) { + + private val nonFederatedGenerator = UcGeneratorNonFederated(context, scopeProvider) + val federates = mutableListOf() + + // Compute the total number of events and reactions within a federate. This reuses the + // computation for the non-federated case, but federated connections are also considered. + fun totalNumEventsAndReactionsFederated(federate: UcFederate): Pair { + val eventsFromFederatedConnections = maxNumPendingEvents[mainDef.reactor]!! + val eventsAndReactionsInFederate = nonFederatedGenerator.totalNumEventsAndReactions(federate.inst.reactor) + return Pair( + eventsFromFederatedConnections + eventsAndReactionsInFederate.first, + eventsAndReactionsInFederate.second + ) + } + + private fun doGenerateFederate( + resource: Resource, + context: LFGeneratorContext, + srcGenPath: Path, + federate: UcFederate + ): GeneratorResult.Status { + if (!canGenerate(errorsOccurred(), federate.inst, messageReporter, context)) return GeneratorResult.Status.FAILED + + // generate header and source files for all reactors + getAllInstantiatedReactors(federate.inst.reactor).map { + nonFederatedGenerator.generateReactorFiles( + it, + srcGenPath + ) + } + + generateFederateFiles(federate, srcGenPath) + + // Collect the info on the generated sources + ucSources.addAll(nonFederatedGenerator.ucSources) + codeMaps.putAll(nonFederatedGenerator.codeMaps) + + for (r in getAllImportedResources(resource)) { + val generator = UcPreambleGenerator(r, fileConfig, scopeProvider) + val headerFile = fileConfig.getPreambleHeaderPath(r) + val preambleCodeMap = CodeMap.fromGeneratedCode(generator.generateHeader()) + codeMaps[srcGenPath.resolve(headerFile)] = preambleCodeMap + FileUtil.writeToFile(preambleCodeMap.generatedCode, srcGenPath.resolve(headerFile), true) + } + return GeneratorResult.Status.GENERATED + } + + // The same UcGeneratorFederated is used to iteratively generated a project for each federate. + // After generating we need to clear certain state variables before starting with the next federate. + private fun clearStateFromPreviousFederate() { + codeMaps.clear() + ucSources.clear() + maxNumPendingEvents.clear() + nonFederatedGenerator.maxNumPendingEvents.clear() + nonFederatedGenerator.codeMaps.clear() + nonFederatedGenerator.ucSources.clear() + } + + // This function creates an instantiation for the top-level main reactor. + private fun createMainDef() { + for (reactor in reactors) { + if (reactor.isFederated) { // Note that this will be the "main" top-level reactor. Not each federate. + this.mainDef = LfFactory.eINSTANCE.createInstantiation() + this.mainDef.name = reactor.name + this.mainDef.reactorClass = reactor + } + } + } + + override fun doGenerate(resource: Resource, context: LFGeneratorContext) { + super.doGenerate(resource, context) + createMainDef() + for (inst in getAllFederates()) { + for (bankIdx in 0.., + targetConfig: TargetConfig, + private val fileConfig: UcFileConfig, +): UcMainGenerator(targetConfig) { - fun generateStartHeader() = with(PrependOperator) { - """ - |#ifndef REACTOR_UC_LF_MAIN_H - |#define REACTOR_UC_LF_MAIN_H - | - |void lf_start(void); - | - |#endif - | - """.trimMargin() - } - - fun generateMainSource() = with(PrependOperator) { + private val top = currentFederate.inst.eContainer() as Reactor + private val ucConnectionGenerator = UcConnectionGenerator(top, currentFederate, otherFederates) + override fun generateStartSource() = with(PrependOperator) { """ - |#include "lf_start.h" - |int main(void) { - | lf_start(); + |#include "reactor-uc/reactor-uc.h" + |#include "lf_federate.h" + |static ${currentFederate.codeType} main_reactor; + |static Environment lf_environment; + |Environment *_lf_environment = &lf_environment; + |void lf_exit(void) { + | Environment_free(&lf_environment); + |} + |void lf_start(void) { + | Environment_ctor(&lf_environment, (Reactor *)&main_reactor); + | lf_environment.scheduler->duration = ${getDuration()}; + | lf_environment.scheduler->keep_alive = ${keepAlive()}; + | lf_environment.scheduler->leader = ${top.instantiations.first() == currentFederate.inst && currentFederate.bankIdx == 0}; + | lf_environment.fast_mode = ${fast()}; + | lf_environment.has_async_events = ${currentFederate.inst.reactor.inputs.isNotEmpty()}; + | ${currentFederate.codeType}_ctor(&main_reactor, NULL, &lf_environment); + | lf_environment.net_bundles_size = ${ucConnectionGenerator.getNumFederatedConnectionBundles()}; + | lf_environment.net_bundles = (FederatedConnectionBundle **) &main_reactor._bundles; + | lf_environment.assemble(&lf_environment); + | lf_environment.start(&lf_environment); + | lf_exit(); |} """.trimMargin() } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt index 4acbf6ff..abb6d354 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt @@ -5,16 +5,18 @@ import org.lflang.target.TargetConfig import org.lflang.generator.PrependOperator import org.lflang.joinWithLn import org.lflang.lf.Reactor -import org.lflang.target.property.BuildTypeProperty import org.lflang.toUnixString import java.nio.file.Path -import java.time.LocalDateTime import kotlin.io.path.name import kotlin.math.max -class UcMakeGenerator(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig, private val numEvents: Int, private val numReactions: Int) { +abstract class UcMakeGenerator() { + abstract fun generateMake(sources: List): String +} + +class UcMakeGeneratorNonFederated(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig, private val numEvents: Int, private val numReactions: Int): UcMakeGenerator() { private val S = '$' // a little trick to escape the dollar sign with $S - fun generateMake(sources: List) = with(PrependOperator) { + override fun generateMake(sources: List) = with(PrependOperator) { val sources = sources.filterNot { it.name=="lf_main.c" } """ | # Makefile generated for ${fileConfig.name} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt index 7d0038c2..89e90af2 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -2,23 +2,21 @@ package org.lflang.generator.uc import org.lflang.AttributeUtils.getInterfaceAttributes import org.lflang.AttributeUtils.getLinkAttribute -import java.util.concurrent.atomic.AtomicInteger import org.lflang.generator.uc.NetworkChannelType.* import org.lflang.lf.Attribute -import java.math.BigInteger +// An enumeration of the supported NetworkChannels enum class NetworkChannelType { TCP_IP, CUSTOM, COAP_UDP_IP, NONE } -abstract class UcNetworkEndpoint(val iface: UcNetworkInterface) object UcNetworkInterfaceFactory { private val creators: Map UcNetworkInterface> = mapOf( - Pair(TCP_IP, { federate, attr -> UcTcpIpInterface.fromAttribute(federate, attr) }), - Pair(COAP_UDP_IP, { federate, attr -> UcCoapUdpIpInterface.fromAttribute(federate, attr) }), - Pair(CUSTOM, { federate, attr -> UcCustomInterface.fromAttribute(federate, attr) }) + Pair(TCP_IP) { federate, attr -> UcTcpIpInterface.fromAttribute(federate, attr) }, + Pair(COAP_UDP_IP) { federate, attr -> UcCoapUdpIpInterface.fromAttribute(federate, attr) }, + Pair(CUSTOM) { federate, attr -> UcCustomInterface.fromAttribute(federate, attr) } ) fun createInterfaces(federate: UcFederate): List { @@ -26,11 +24,11 @@ object UcNetworkInterfaceFactory { return if (attrs.isEmpty()) { listOf(createDefaultInterface()) } else { - attrs.map { createInterface(federate, it) } + attrs.map { createInterfaceFromAttribute(federate, it) } } } - fun createInterface(federate: UcFederate, attr: Attribute): UcNetworkInterface { + private fun createInterfaceFromAttribute(federate: UcFederate, attr: Attribute): UcNetworkInterface { val protocol = attr.attrName.substringAfter("_") return when (protocol) { "tcp" -> creators.get(TCP_IP)!!.invoke(federate, attr) @@ -40,16 +38,25 @@ object UcNetworkInterfaceFactory { } } - fun createDefaultInterface(): UcNetworkInterface = UcTcpIpInterface(ipAddress = IPAddress.fromString("127.0.0.1")) + private fun createDefaultInterface(): UcNetworkInterface = UcTcpIpInterface(ipAddress = IPAddress.fromString("127.0.0.1")) } +// A NetworkEndpoint is a communication endpoint located at the UcNetworkInterface of a federate. +// A NetworkChannel is between two NetworkEndpoints. +abstract class UcNetworkEndpoint(val iface: UcNetworkInterface) class UcTcpIpEndpoint(val ipAddress: IPAddress, val port: Int, iface: UcTcpIpInterface) : UcNetworkEndpoint(iface) {} class UcCoapUdpIpEndpoint(val ipAddress: IPAddress, iface: UcCoapUdpIpInterface) : UcNetworkEndpoint(iface) {} -class UcCustomEndpoint(val _iface: UcCustomInterface) : UcNetworkEndpoint(_iface) {} +class UcCustomEndpoint(iface: UcCustomInterface) : UcNetworkEndpoint(iface) {} +// A federate can have several NetworkInterfaces, which are specified using attributes in the LF program. +// A NetworkInterface has a name and can contain a set of endpoints. abstract class UcNetworkInterface(val type: NetworkChannelType, val name: String) { val endpoints = mutableListOf() + + /** A header file that should be included to support this NetworkInterface. Used by CustomInterface */ abstract val includeHeaders: String + + /** A compile definition which must be defined to get support for this NetworkInterface*/ abstract val compileDefs: String } @@ -143,37 +150,48 @@ class UcCustomInterface(name: String, val include: String, val args: String? = n } } +/** A UcNetworkChannel is created by giving two endpoints and deciding which one is the server */ abstract class UcNetworkChannel( val type: NetworkChannelType, val src: UcNetworkEndpoint, val dest: UcNetworkEndpoint, val serverLhs: Boolean, ) { + /** Generate code calling the constructor of the source endpoint */ abstract fun generateChannelCtorSrc(): String + /** Generate code calling the constructor of the destination endpoint */ abstract fun generateChannelCtorDest(): String abstract val codeType: String companion object { - fun createNetworkChannelForBundle(bundle: UcFederatedConnectionBundle): UcNetworkChannel { + /** Given a FederatedConnection bundle which contains an LF connection and + * all the connection channels. Create an endpoint at source and destination and a UcNetworkChannel + * connecting the, + */ + fun createNetworkEndpointsAndChannelForBundle(bundle: UcFederatedConnectionBundle): UcNetworkChannel { val attr: Attribute? = getLinkAttribute(bundle.groupedConnections.first().lfConn) var srcIf: UcNetworkInterface var destIf: UcNetworkInterface var channel: UcNetworkChannel - var serverLhs: Boolean = true + var serverLhs = true var serverPort: Int? = null if (attr == null) { + // If there is no @link attribute on the connection we just get the default (unless there + // is ambiguity) srcIf = bundle.src.getDefaultInterface() destIf = bundle.dest.getDefaultInterface() } else { + // Parse the @link attribute and generate a UcNetworkChannel between the correct + // interfaces. val srcIfName = attr.getParamString("left") val destIfName = attr.getParamString("right") val serverSideAttr = attr.getParamString("server_side") serverPort = attr.getParamInt("server_port") srcIf = - if (srcIfName != null) bundle.src.getInterface(srcIfName!!) else bundle.src.getDefaultInterface() + if (srcIfName != null) bundle.src.getInterface(srcIfName) else bundle.src.getDefaultInterface() destIf = - if (destIfName != null) bundle.dest.getInterface(destIfName!!) else bundle.dest.getDefaultInterface() + if (destIfName != null) bundle.dest.getInterface(destIfName) else bundle.dest.getDefaultInterface() serverLhs = if (serverSideAttr == null) true else !serverSideAttr!!.equals("right") } @@ -245,17 +263,17 @@ class UcCustomChannel( dest: UcCustomEndpoint, serverLhs: Boolean = true, ) : UcNetworkChannel(CUSTOM, src, dest, serverLhs) { - private val srcName = src.iface.name - private val destName = dest.iface.name - private val srcArgs = if (src._iface.args != null) ", ${src._iface.args}" else "" - private val destArgs = if (dest._iface.args != null) ", ${dest._iface.args}" else "" + val srcIface = src.iface as UcCustomInterface + val destIface = dest.iface as UcCustomInterface + private val srcArgs = if (srcIface.args != null) ", ${srcIface.args}" else "" + private val destArgs = if (destIface.args != null) ", ${destIface.args}" else "" override fun generateChannelCtorSrc() = - "${srcName}_ctor(&self->channel, parent->env ${srcArgs});" + "${srcIface.name}_ctor(&self->channel, parent->env ${srcArgs});" override fun generateChannelCtorDest() = - "${destName}_ctor(&self->channel, parent->env ${destArgs});" + "${destIface.name}_ctor(&self->channel, parent->env ${destArgs});" override val codeType: String - get() = srcName + get() = srcIface.name } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt index b5e2e837..defa85bc 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGenerator.kt @@ -1,36 +1,207 @@ package org.lflang.generator.uc import org.lflang.MessageReporter +import org.lflang.generator.CodeMap import org.lflang.target.TargetConfig import org.lflang.generator.GeneratorCommandFactory import org.lflang.generator.LFGeneratorContext import org.lflang.generator.uc.UcInstanceGenerator.Companion.isAFederate import org.lflang.reactor import org.lflang.target.property.BuildTypeProperty +import org.lflang.target.property.type.BuildTypeType.BuildType import org.lflang.toDefinition import org.lflang.toUnixString +import org.lflang.util.FileUtil +import org.lflang.util.LFCommand +import java.nio.file.Files import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.createSymbolicLinkPointingTo /** Abstract class for generating platform specific files and invoking the target compiler. */ abstract class UcPlatformGenerator(protected val generator: UcGenerator) { - protected val codeMaps = generator.codeMaps - protected val ucSources = generator.ucSources + private val codeMaps = generator.codeMaps + private val ucSources = generator.ucSources protected val messageReporter: MessageReporter = generator.messageReporter protected val fileConfig: UcFileConfig = generator.fileConfig protected val targetConfig: TargetConfig = generator.targetConfig - protected val commandFactory: GeneratorCommandFactory = generator.commandFactory + private val commandFactory: GeneratorCommandFactory = generator.commandFactory protected val mainReactor = generator.mainDef.reactorClass.toDefinition() + abstract val buildPath: Path + abstract val srcGenPath: Path + abstract val targetName: String - - protected val relativeBinDir = fileConfig.outPath.relativize(fileConfig.binPath).toUnixString() + private val relativeBinDir = fileConfig.outPath.relativize(fileConfig.binPath).toUnixString() abstract fun generatePlatformFiles() - abstract fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean = false): Boolean - - protected val cmakeArgs: List + private val cmakeArgs: List get() = listOf( "-DCMAKE_BUILD_TYPE=${targetConfig.get(BuildTypeProperty.INSTANCE)}", ) + companion object { + fun buildTypeToCmakeConfig(type: BuildType) = when (type) { + BuildType.TEST -> "Debug" + else -> type.toString() + } + } + + fun doGeneratePlatformFiles(mainGenerator: UcMainGenerator, cmakeGenerator: UcCmakeGenerator, makeGenerator: UcMakeGenerator) { + val reactorUCEnvPath = System.getenv("REACTOR_UC_PATH") + if (reactorUCEnvPath == null) { + messageReporter.nowhere().error("REACTOR_UC_PATH environment variable not defined. Do source env.bash in reactor-uc") + return; + } + val runtimePath: Path = Paths.get(reactorUCEnvPath) + + val startSourceFile = Paths.get("lf_start.c") + val startHeaderFile = Paths.get("lf_start.h") + val mainSourceFile = Paths.get("lf_main.c") + + val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateStartSource()) + val mainCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateMainSource()) + + ucSources.addAll(listOf(startSourceFile, mainSourceFile)) + codeMaps[srcGenPath.resolve(startSourceFile)] = startCodeMap + codeMaps[srcGenPath.resolve(mainSourceFile)] = mainCodeMap + + FileUtil.writeToFile(startCodeMap.generatedCode, srcGenPath.resolve(startSourceFile), true) + FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) + FileUtil.writeToFile(mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) + + FileUtil.writeToFile(cmakeGenerator.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) + val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); + try { + runtimeSymlinkPath.createSymbolicLinkPointingTo(runtimePath); + } catch (e: Exception) { + // Do nothing + } + + FileUtil.writeToFile(makeGenerator.generateMake(ucSources), srcGenPath.resolve("Makefile"), true) + } + + fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean = false): Boolean { + // make sure the build directory exists + Files.createDirectories(buildPath) + + val version = checkCmakeVersion() + var parallelize = true + if (version != null && version.compareVersion("3.12.0") < 0) { + messageReporter.nowhere().warning("CMAKE is older than version 3.12. Parallel building is not supported.") + parallelize = false + } + + if (version != null) { + val cmakeReturnCode = runCmake(context) + + if (cmakeReturnCode == 0 && !onlyGenerateBuildFiles) { + // If cmake succeeded, run make + val makeCommand = createMakeCommand(buildPath, parallelize, targetName) + val makeReturnCode = UcValidator(fileConfig, messageReporter, codeMaps).run(makeCommand, context.cancelIndicator) + var installReturnCode = 0 + if (makeReturnCode == 0) { + val installCommand = createMakeCommand(buildPath, parallelize, "install") + installReturnCode = installCommand.run(context.cancelIndicator) + if (installReturnCode == 0) { + println("SUCCESS (compiling generated C code)") + println("Generated source code is in ${fileConfig.srcGenPath}") + println("Compiled binary is in ${fileConfig.binPath}") + } + } + if ((makeReturnCode != 0 || installReturnCode != 0) && !messageReporter.errorsOccurred) { + // If errors occurred but none were reported, then the following message is the best we can do. + messageReporter.nowhere().error("make failed with error code $makeReturnCode") + } + } + if (cmakeReturnCode != 0) { + messageReporter.nowhere().error("cmake failed with error code $cmakeReturnCode") + } + } + return !messageReporter.errorsOccurred + } + + private fun checkCmakeVersion(): String? { + // get the installed cmake version and make sure it is at least 3.5 + val cmd = commandFactory.createCommand("cmake", listOf("--version"), buildPath) + var version: String? = null + if (cmd != null && cmd.run() == 0) { + val regex = "\\d+(\\.\\d+)+".toRegex() + version = regex.find(cmd.output.toString())?.value + } + if (version == null || version.compareVersion("3.5.0") < 0) { + messageReporter.nowhere( + ).error( + "The uC target requires CMAKE >= 3.5.0 to compile the generated code. " + + "Auto-compiling can be disabled using the \"no-compile: true\" target property." + ) + return null + } + + return version + } + private fun runCmake(context: LFGeneratorContext): Int { + val cmakeCommand = createCmakeCommand(buildPath, fileConfig.outPath) + return cmakeCommand.run(context.cancelIndicator) + } + + private fun String.compareVersion(other: String): Int { + val a = this.split(".").map { it.toInt() } + val b = other.split(".").map { it.toInt() } + for (x in (a zip b)) { + val res = x.first.compareTo(x.second) + if (res != 0) + return res + } + return 0 + } + + private fun getMakeArgs(buildPath: Path, parallelize: Boolean, target: String): List { + val cmakeConfig = buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE)) + val makeArgs = mutableListOf( + "--build", + buildPath.fileName.toString(), + "--config", + cmakeConfig, + "--target", + target + ) + + if (parallelize) { + makeArgs.addAll(listOf("--parallel", Runtime.getRuntime().availableProcessors().toString())) + } + + return makeArgs + } + private fun createMakeCommand(buildPath: Path, parallelize: Boolean, target: String): LFCommand { + val makeArgs = getMakeArgs(buildPath, parallelize, target) + return commandFactory.createCommand("cmake", makeArgs, buildPath.parent) + } + + private fun getCmakeArgs( + buildPath: Path, + outPath: Path, + sourcesRoot: String? = null + ) = cmakeArgs + listOf( + "-DCMAKE_INSTALL_PREFIX=${outPath.toUnixString()}", + "-DCMAKE_INSTALL_BINDIR=$relativeBinDir", + "--fresh", + "-S", + sourcesRoot ?: srcGenPath.toUnixString(), + "-B", + buildPath.fileName.toString() + ) + + private fun createCmakeCommand( + buildPath: Path, + outPath: Path, + sourcesRoot: String? = null + ): LFCommand { + val cmd = commandFactory.createCommand( + "cmake", + getCmakeArgs(buildPath, outPath, sourcesRoot), + buildPath.parent + ) + return cmd + } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt new file mode 100644 index 00000000..1c611820 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt @@ -0,0 +1,30 @@ +package org.lflang.generator.uc + +import org.lflang.reactor +import org.lflang.util.FileUtil +import java.nio.file.Path + +class UcPlatformGeneratorFederated(generator: UcGeneratorFederated, override val srcGenPath: Path, private val federate: UcFederate) : + UcPlatformGenerator(generator) { + + override val buildPath = srcGenPath.resolve("build") + override val targetName: String = federate.codeType + + override fun generatePlatformFiles() { + val generator = generator as UcGeneratorFederated + val mainGenerator = UcMainGeneratorFederated(federate, generator.federates, generator.targetConfig, generator.fileConfig) + val numEventsAndReactions = generator.totalNumEventsAndReactionsFederated(federate) + val cmakeGenerator = UcCmakeGeneratorFederated(federate, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) + val makeGenerator = UcMakeGeneratorNonFederated(federate.inst.reactor, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) + super.doGeneratePlatformFiles(mainGenerator, cmakeGenerator, makeGenerator) + + generateLaunchScript() + } + + fun generateLaunchScript() { + val generator = generator as UcGeneratorFederated + val launchScriptGenerator = UcFederatedLaunchScriptGenerator(fileConfig) + FileUtil.writeToFile(launchScriptGenerator.generateLaunchScript(generator.federates), fileConfig.binPath.resolve(fileConfig.name)) + fileConfig.binPath.resolve(fileConfig.name).toFile().setExecutable(true) + } +} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt new file mode 100644 index 00000000..f6b95f73 --- /dev/null +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorNonFederated.kt @@ -0,0 +1,29 @@ +package org.lflang.generator.uc + +import org.lflang.generator.CodeMap +import org.lflang.generator.LFGeneratorContext +import org.lflang.reactor +import org.lflang.target.property.BuildTypeProperty +import org.lflang.target.property.type.BuildTypeType.BuildType +import org.lflang.toUnixString +import org.lflang.util.FileUtil +import org.lflang.util.LFCommand +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.createSymbolicLinkPointingTo + +class UcPlatformGeneratorNonFederated(generator: UcGenerator, override val srcGenPath: Path) : + UcPlatformGenerator(generator) { + + override val buildPath = srcGenPath.resolve("build") + override val targetName = fileConfig.name + + override fun generatePlatformFiles() { + val mainGenerator = UcMainGeneratorNonFederated(mainReactor, generator.targetConfig, generator.fileConfig) + val numEventsAndReactions = generator.totalNumEventsAndReactions(generator.mainDef.reactor) + val cmakeGenerator = UcCmakeGeneratorNonFederated(generator.mainDef, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) + val makeGenerator = UcMakeGeneratorNonFederated(mainReactor, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) + super.doGeneratePlatformFiles(mainGenerator, cmakeGenerator, makeGenerator) + } +} \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt deleted file mode 100644 index 63b88ad4..00000000 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcStandalonePlatformGenerator.kt +++ /dev/null @@ -1,198 +0,0 @@ -package org.lflang.generator.uc - -import org.lflang.generator.CodeMap -import org.lflang.generator.LFGeneratorContext -import org.lflang.reactor -import org.lflang.target.property.BuildTypeProperty -import org.lflang.target.property.type.BuildTypeType.BuildType -import org.lflang.toUnixString -import org.lflang.util.FileUtil -import org.lflang.util.LFCommand -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import kotlin.io.path.createSymbolicLinkPointingTo - -class UcStandalonePlatformGenerator(generator: UcGenerator, val srcGenPath: Path) : - UcPlatformGenerator(generator) { - - companion object { - fun buildTypeToCmakeConfig(type: BuildType) = when (type) { - BuildType.TEST -> "Debug" - else -> type.toString() - } - } - - override fun generatePlatformFiles() { - val reactorUCEnvPath = System.getenv("REACTOR_UC_PATH") - if (reactorUCEnvPath == null) { - messageReporter.nowhere().error("REACTOR_UC_PATH environment variable not defined. Do source env.bash in reactor-uc") - return; - } - val runtimePath: Path = Paths.get(reactorUCEnvPath) - // generate the main source file (containing main()) - val mainGenerator = UcMainGenerator(mainReactor, generator.targetConfig, generator.fileConfig) - - val startSourceFile = Paths.get("lf_start.c") - val startHeaderFile = Paths.get("lf_start.h") - val mainSourceFile = Paths.get("lf_main.c") - - val startCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateStartSource()) - val mainCodeMap = CodeMap.fromGeneratedCode(mainGenerator.generateMainSource()) - - ucSources.addAll(listOf(startSourceFile, mainSourceFile)) - codeMaps[srcGenPath.resolve(startSourceFile)] = startCodeMap - codeMaps[srcGenPath.resolve(mainSourceFile)] = mainCodeMap - - FileUtil.writeToFile(startCodeMap.generatedCode, srcGenPath.resolve(startSourceFile), true) - FileUtil.writeToFile(mainCodeMap.generatedCode, srcGenPath.resolve(mainSourceFile), true) - FileUtil.writeToFile(mainGenerator.generateStartHeader(), srcGenPath.resolve(startHeaderFile), true) - - val numEventsAndReactions = generator.totalNumEventsAndReactions(generator.mainDef.reactor) - - val cmakeGenerator = UcCmakeGenerator(generator.mainDef, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) - val makeGenerator = UcMakeGenerator(mainReactor, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) - FileUtil.writeToFile(cmakeGenerator.generateCmake(ucSources), srcGenPath.resolve("CMakeLists.txt"), true) - val runtimeSymlinkPath: Path = srcGenPath.resolve("reactor-uc"); - try { - runtimeSymlinkPath.createSymbolicLinkPointingTo(runtimePath); - } catch (e: Exception) { - // Do nothing - } - - FileUtil.writeToFile(makeGenerator.generateMake(ucSources), srcGenPath.resolve("Makefile"), true) - } - - override fun doCompile(context: LFGeneratorContext, onlyGenerateBuildFiles: Boolean): Boolean { - - // make sure the build directory exists - Files.createDirectories(fileConfig.buildPath) - - val version = checkCmakeVersion() - var parallelize = true - if (version != null && version.compareVersion("3.12.0") < 0) { - messageReporter.nowhere().warning("CMAKE is older than version 3.12. Parallel building is not supported.") - parallelize = false - } - - if (version != null) { - val cmakeReturnCode = runCmake(context) - - if (cmakeReturnCode == 0 && !onlyGenerateBuildFiles) { - // If cmake succeeded, run make - val makeCommand = createMakeCommand(fileConfig.buildPath, parallelize, fileConfig.name) - val makeReturnCode = UcValidator(fileConfig, messageReporter, codeMaps).run(makeCommand, context.cancelIndicator) - var installReturnCode = 0 - if (makeReturnCode == 0) { - val installCommand = createMakeCommand(fileConfig.buildPath, parallelize, "install") - installReturnCode = installCommand.run(context.cancelIndicator) - if (installReturnCode == 0) { - println("SUCCESS (compiling generated C code)") - println("Generated source code is in ${fileConfig.srcGenPath}") - println("Compiled binary is in ${fileConfig.binPath}") - } - } - if ((makeReturnCode != 0 || installReturnCode != 0) && !messageReporter.errorsOccurred) { - // If errors occurred but none were reported, then the following message is the best we can do. - messageReporter.nowhere().error("make failed with error code $makeReturnCode") - } - } - if (cmakeReturnCode != 0) { - messageReporter.nowhere().error("cmake failed with error code $cmakeReturnCode") - } - } - return !messageReporter.errorsOccurred - } - - private fun checkCmakeVersion(): String? { - // get the installed cmake version and make sure it is at least 3.5 - val cmd = commandFactory.createCommand("cmake", listOf("--version"), fileConfig.buildPath) - var version: String? = null - if (cmd != null && cmd.run() == 0) { - val regex = "\\d+(\\.\\d+)+".toRegex() - version = regex.find(cmd.output.toString())?.value - } - if (version == null || version.compareVersion("3.5.0") < 0) { - messageReporter.nowhere( - ).error( - "The uC target requires CMAKE >= 3.5.0 to compile the generated code. " + - "Auto-compiling can be disabled using the \"no-compile: true\" target property." - ) - return null - } - - return version - } - - - /** - * Run CMake to generate build files. - * @return True, if cmake run successfully - */ - private fun runCmake(context: LFGeneratorContext): Int { - val cmakeCommand = createCmakeCommand(fileConfig.buildPath, fileConfig.outPath) - return cmakeCommand.run(context.cancelIndicator) - } - - private fun String.compareVersion(other: String): Int { - val a = this.split(".").map { it.toInt() } - val b = other.split(".").map { it.toInt() } - for (x in (a zip b)) { - val res = x.first.compareTo(x.second) - if (res != 0) - return res - } - return 0 - } - - private fun getMakeArgs(buildPath: Path, parallelize: Boolean, target: String): List { - val cmakeConfig = buildTypeToCmakeConfig(targetConfig.get(BuildTypeProperty.INSTANCE)) - val makeArgs = mutableListOf( - "--build", - buildPath.fileName.toString(), - "--config", - cmakeConfig, - "--target", - target - ) - - if (parallelize) { - makeArgs.addAll(listOf("--parallel", Runtime.getRuntime().availableProcessors().toString())) - } - - return makeArgs - } - - - private fun createMakeCommand(buildPath: Path, parallelize: Boolean, target: String): LFCommand { - val makeArgs = getMakeArgs(buildPath, parallelize, target) - return commandFactory.createCommand("cmake", makeArgs, buildPath.parent) - } - - private fun getCmakeArgs( - buildPath: Path, - outPath: Path, - sourcesRoot: String? = null - ) = cmakeArgs + listOf( - "-DCMAKE_INSTALL_PREFIX=${outPath.toUnixString()}", - "-DCMAKE_INSTALL_BINDIR=$relativeBinDir", - "--fresh", - "-S", - sourcesRoot ?: srcGenPath.toUnixString(), - "-B", - buildPath.fileName.toString() - ) - - private fun createCmakeCommand( - buildPath: Path, - outPath: Path, - sourcesRoot: String? = null - ): LFCommand { - val cmd = commandFactory.createCommand( - "cmake", - getCmakeArgs(buildPath, outPath, sourcesRoot), - buildPath.parent - ) - return cmd - } -} \ No newline at end of file diff --git a/test/lf/src/FederatedAttr.lf b/test/lf/src/FederatedAttr.lf index 5b0fdd82..e59db42c 100644 --- a/test/lf/src/FederatedAttr.lf +++ b/test/lf/src/FederatedAttr.lf @@ -1,6 +1,6 @@ target uC { platform: Native, - timeout: 1 msec + timeout: 1 sec } reactor Src(id: int = 0) { diff --git a/test/lf/src/FederatedBank.lf b/test/lf/src/FederatedBank.lf index 8cacfcd8..d30d16db 100644 --- a/test/lf/src/FederatedBank.lf +++ b/test/lf/src/FederatedBank.lf @@ -1,6 +1,7 @@ target uC { platform: Native, - timeout: 1 msec + timeout: 1 sec, + logging: Debug } @@ -38,6 +39,7 @@ reactor Fed(bank_idx: int = 0) { reaction(shutdown) {= validate(self->check2); validate(self->check1); + printf("Federate %d is shutting down\n", self->bank_idx); =} } diff --git a/test/lf/src/FederatedConnection.lf b/test/lf/src/FederatedConnection.lf index 5bd958b7..95164d63 100644 --- a/test/lf/src/FederatedConnection.lf +++ b/test/lf/src/FederatedConnection.lf @@ -1,5 +1,6 @@ target uC { platform: Native, + timeout: 1sec } reactor Src(id: int = 0) { From 42fc1add1d10b125b8b9b5c83942c09dd0ac2432 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 15:46:05 +0100 Subject: [PATCH 29/65] More docs --- .../lflang/generator/uc/UcConnectionGenerator.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt index 244582ec..387eb703 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcConnectionGenerator.kt @@ -138,10 +138,8 @@ class UcConnectionGenerator( } else { UcChannelQueue(portVarRef, emptyList()) } - } - companion object { private val Connection.delayString get(): String = this.delay.orNever().toCCode() @@ -151,6 +149,11 @@ class UcConnectionGenerator( /** A global list of FederatedConnectionBundles. It is computed once and reused when code-generating */ private var allFederatedConnectionBundles: List = emptyList() + /** + * This function takes a list of grouped connections and creates the necessary FederatedConnectionBundles. + * The bundles are written to the global variable allFederatedConnectionBundles and shared accross federates. + * Thus, this function should only be called once during code-gen. + */ private fun createFederatedConnectionBundles(groupedConnections: List) { val groupedSet = HashSet(groupedConnections) val bundles = mutableListOf() @@ -181,6 +184,7 @@ class UcConnectionGenerator( } init { + // Only pass through all federates and add NetworkInterface objects to them once. if (isFederated && !federateInterfacesInitialized) { for (fed in allFederates) { UcNetworkInterfaceFactory.createInterfaces(fed).forEach { fed.addInterface(it) } @@ -188,7 +192,7 @@ class UcConnectionGenerator( federateInterfacesInitialized = true } - // Only parse out federated connection bundles once for the very first federate + // Parse out all GroupedConnections. Note that this is repeated for each federate. val channels = mutableListOf() reactor.allConnections.forEach { channels.addAll(parseConnectionChannels(it, allFederates)) } val grouped = groupConnections(channels) @@ -212,10 +216,11 @@ class UcConnectionGenerator( .filter { it.channels.fold(true) { acc, c -> acc && (c.src.federate == currentFederate) } } ) } else { + // In the non-federated case, all grouped connections are handled togehter. nonFederatedConnections.addAll(grouped) } - // Assign a unique ID to each connection to avoid possible naming conflicts. + // Assign a unique ID to each connection to avoid possible naming conflicts in the generated code. val allGroupedConnections = federatedConnectionBundles.map { it.groupedConnections }.flatten().plus(nonFederatedConnections) allGroupedConnections.forEachIndexed { idx, el -> From 9fee19b60cb1dfef846e8dad7c8292eca044d116 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 15:50:01 +0100 Subject: [PATCH 30/65] CI --- examples/posix/hello/hello.c | 2 +- include/reactor-uc/macros.h | 42 ++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/posix/hello/hello.c b/examples/posix/hello/hello.c index 8ec71d4d..122dd145 100644 --- a/examples/posix/hello/hello.c +++ b/examples/posix/hello/hello.c @@ -4,7 +4,7 @@ LF_DEFINE_REACTION_BODY(TimerSource, r) { LF_SCOPE_SELF(TimerSource); LF_SCOPE_ENV(); - printf("TimerSource World @ %lld\n", env->get_elapsed_logical_time(env)); + printf("TimerSource World @ %"PRId64"\n", env->get_elapsed_logical_time(env)); } int main() { diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index 5163632e..1b3237f2 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -653,35 +653,35 @@ typedef struct FederatedInputConnection FederatedInputConnection; #define LF_ENTRY_POINT(MainReactorName, Timeout, KeepAlive, Fast) \ MainReactorName main_reactor; \ - Environment env; \ - void lf_exit(void) { Environment_free(&env); } \ + Environment _lf_environment; \ + void lf_exit(void) { Environment_free(&_lf_environment); } \ void lf_start() { \ - Environment_ctor(&env, (Reactor *)&main_reactor); \ - MainReactorName##_ctor(&main_reactor, NULL, &env); \ - env.scheduler->duration = Timeout; \ - env.scheduler->keep_alive = KeepAlive; \ - env.fast_mode = Fast; \ - env.assemble(&env); \ - env.start(&env); \ + Environment_ctor(&_lf_environment, (Reactor *)&main_reactor); \ + MainReactorName##_ctor(&main_reactor, NULL, &_lf_environment); \ + _lf_environment.scheduler->duration = Timeout; \ + _lf_environment.scheduler->keep_alive = KeepAlive; \ + _lf_environment.fast_mode = Fast; \ + _lf_environment.assemble(&_lf_environment); \ + _lf_environment.start(&_lf_environment); \ lf_exit(); \ } #define LF_ENTRY_POINT_FEDERATED(FederateName, Timeout, KeepAlive, HasInputs, NumBundles, IsLeader) \ FederateName main_reactor; \ - Environment env; \ - void lf_exit(void) { Environment_free(&env); } \ + Environment _lf_environment; \ + void lf_exit(void) { Environment_free(&_lf_environment); } \ void lf_start() { \ - Environment_ctor(&env, (Reactor *)&main_reactor); \ - env.scheduler->duration = Timeout; \ - env.scheduler->keep_alive = KeepAlive; \ - env.scheduler->leader = IsLeader; \ - env.has_async_events = HasInputs; \ + Environment_ctor(&_lf_environment, (Reactor *)&main_reactor); \ + _lf_environment.scheduler->duration = Timeout; \ + _lf_environment.scheduler->keep_alive = KeepAlive; \ + _lf_environment.scheduler->leader = IsLeader; \ + _lf_environment.has_async_events = HasInputs; \ \ - FederateName##_ctor(&main_reactor, NULL, &env); \ - env.net_bundles_size = NumBundles; \ - env.net_bundles = (FederatedConnectionBundle **)&main_reactor._bundles; \ - env.assemble(&env); \ - env.start(&env); \ + FederateName##_ctor(&main_reactor, NULL, &_lf_environment); \ + _lf_environment.net_bundles_size = NumBundles; \ + _lf_environment.net_bundles = (FederatedConnectionBundle **)&main_reactor._bundles; \ + _lf_environment.assemble(&_lf_environment); \ + _lf_environment.start(&_lf_environment); \ lf_exit(); \ } From 97bb16a41ec0bb73c3146a602ac607e16e86aced Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 16:14:06 +0100 Subject: [PATCH 31/65] Fixes --- include/reactor-uc/environment.h | 3 ++- include/reactor-uc/macros.h | 45 ++++++++++++++++---------------- src/logging.c | 6 ++++- test/unit/action_lib.h | 5 ++-- test/unit/deadline_test.c | 2 ++ test/unit/delayed_conn_test.c | 4 ++- test/unit/event_queue_test.c | 1 + test/unit/port_test.c | 4 ++- test/unit/reaction_queue_test.c | 2 ++ test/unit/tcp_channel_test.c | 1 + test/unit/timer_test.c | 1 + 11 files changed, 46 insertions(+), 28 deletions(-) diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index 6b346703..9e02cc00 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -9,7 +9,7 @@ #include "reactor-uc/scheduler.h" typedef struct Environment Environment; -extern Environment *_lf_environment; +extern Environment* _lf_environment; // NOLINT struct Environment { Reactor *main; // The top-level reactor of the program. @@ -61,6 +61,7 @@ struct Environment { void (*request_shutdown)(Environment *self); }; + void Environment_ctor(Environment *self, Reactor *main); void Environment_free(Environment *self); diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index 1b3237f2..775b23d9 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -551,7 +551,6 @@ } typedef struct FederatedOutputConnection FederatedOutputConnection; -// FIXME: What is needed here? #define LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(ReactorName, OutputName, BufferType) \ typedef struct { \ FederatedOutputConnection super; \ @@ -653,35 +652,37 @@ typedef struct FederatedInputConnection FederatedInputConnection; #define LF_ENTRY_POINT(MainReactorName, Timeout, KeepAlive, Fast) \ MainReactorName main_reactor; \ - Environment _lf_environment; \ - void lf_exit(void) { Environment_free(&_lf_environment); } \ + Environment env; \ + Environment *_lf_environment = &env; \ + void lf_exit(void) { Environment_free(&env); } \ void lf_start() { \ - Environment_ctor(&_lf_environment, (Reactor *)&main_reactor); \ - MainReactorName##_ctor(&main_reactor, NULL, &_lf_environment); \ - _lf_environment.scheduler->duration = Timeout; \ - _lf_environment.scheduler->keep_alive = KeepAlive; \ - _lf_environment.fast_mode = Fast; \ - _lf_environment.assemble(&_lf_environment); \ - _lf_environment.start(&_lf_environment); \ + Environment_ctor(&env, (Reactor *)&main_reactor); \ + MainReactorName##_ctor(&main_reactor, NULL, &env); \ + env.scheduler->duration = Timeout; \ + env.scheduler->keep_alive = KeepAlive; \ + env.fast_mode = Fast; \ + env.assemble(&env); \ + env.start(&env); \ lf_exit(); \ } #define LF_ENTRY_POINT_FEDERATED(FederateName, Timeout, KeepAlive, HasInputs, NumBundles, IsLeader) \ FederateName main_reactor; \ - Environment _lf_environment; \ - void lf_exit(void) { Environment_free(&_lf_environment); } \ + Environment env; \ + Environment *_lf_environment = &env; \ + void lf_exit(void) { Environment_free(&env); } \ void lf_start() { \ - Environment_ctor(&_lf_environment, (Reactor *)&main_reactor); \ - _lf_environment.scheduler->duration = Timeout; \ - _lf_environment.scheduler->keep_alive = KeepAlive; \ - _lf_environment.scheduler->leader = IsLeader; \ - _lf_environment.has_async_events = HasInputs; \ + Environment_ctor(&env, (Reactor *)&main_reactor); \ + env.scheduler->duration = Timeout; \ + env.scheduler->keep_alive = KeepAlive; \ + env.scheduler->leader = IsLeader; \ + env.has_async_events = HasInputs; \ \ - FederateName##_ctor(&main_reactor, NULL, &_lf_environment); \ - _lf_environment.net_bundles_size = NumBundles; \ - _lf_environment.net_bundles = (FederatedConnectionBundle **)&main_reactor._bundles; \ - _lf_environment.assemble(&_lf_environment); \ - _lf_environment.start(&_lf_environment); \ + FederateName##_ctor(&main_reactor, NULL, &env); \ + env.net_bundles_size = NumBundles; \ + env.net_bundles = (FederatedConnectionBundle **)&main_reactor._bundles; \ + env.assemble(&env); \ + env.start(&env); \ lf_exit(); \ } diff --git a/src/logging.c b/src/logging.c index bc41fb5d..7ecc9ecb 100644 --- a/src/logging.c +++ b/src/logging.c @@ -61,7 +61,11 @@ void log_message(int level, const char *module, const char *fmt, ...) { break; } #endif - log_printf("%" PRId64 " [%s] [%s] ", _lf_environment->get_elapsed_physical_time(_lf_environment), level_str, module); + instant_t timestamp = 0; + if (_lf_environment) { + timestamp = _lf_environment->get_elapsed_physical_time(_lf_environment); + } + log_printf("%" PRId64 " [%s] [%s] ", timestamp, level_str, module); Platform_vprintf(fmt, args); #ifdef LF_COLORIZE_LOGS log_printf(ANSI_COLOR_RESET); diff --git a/test/unit/action_lib.h b/test/unit/action_lib.h index ff4d87b7..12bb086a 100644 --- a/test/unit/action_lib.h +++ b/test/unit/action_lib.h @@ -48,10 +48,11 @@ LF_REACTOR_CTOR_SIGNATURE(ActionLib) { self->cnt = 0; } +ActionLib my_reactor; +Environment env; +Environment *_lf_environment = &env; void action_lib_start(interval_t duration) { - ActionLib my_reactor; - Environment env; Environment_ctor(&env, (Reactor *)&my_reactor); ActionLib_ctor(&my_reactor, NULL, &env); env.scheduler->duration = duration; diff --git a/test/unit/deadline_test.c b/test/unit/deadline_test.c index 22b4a6a9..789761c2 100644 --- a/test/unit/deadline_test.c +++ b/test/unit/deadline_test.c @@ -45,6 +45,8 @@ LF_REACTOR_CTOR_SIGNATURE(TimerTest) { TimerTest my_reactor; Environment env; +Environment* _lf_environment = &env; + void test_simple() { Environment_ctor(&env, (Reactor *)&my_reactor); env.scheduler->duration = MSEC(100); diff --git a/test/unit/delayed_conn_test.c b/test/unit/delayed_conn_test.c index f359c085..adea5ec7 100644 --- a/test/unit/delayed_conn_test.c +++ b/test/unit/delayed_conn_test.c @@ -102,9 +102,11 @@ LF_REACTOR_CTOR_SIGNATURE(Main) { LF_CONN_REGISTER_DOWNSTREAM(sender_out, 1,1, self->receiver, in, 1, 1); } +Environment env; +Environment* _lf_environment = &env; + void test_simple() { Main main; - Environment env; Environment_ctor(&env, (Reactor *)&main); Main_ctor(&main, NULL, &env); env.scheduler->duration = MSEC(100); diff --git a/test/unit/event_queue_test.c b/test/unit/event_queue_test.c index b449784c..b9a44545 100644 --- a/test/unit/event_queue_test.c +++ b/test/unit/event_queue_test.c @@ -31,6 +31,7 @@ void test_insert(void) { TEST_ASSERT_EQUAL(lf_tag_compare(eptr.tag, e3.tag), 0); } +Environment * _lf_environment = NULL; int main(void) { UNITY_BEGIN(); RUN_TEST(test_insert); diff --git a/test/unit/port_test.c b/test/unit/port_test.c index 52bbfb8d..f90f98b9 100644 --- a/test/unit/port_test.c +++ b/test/unit/port_test.c @@ -101,9 +101,11 @@ LF_REACTOR_CTOR_SIGNATURE(Main) { LF_CONN_REGISTER_DOWNSTREAM(sender_out, 1,1,self->receiver, in, 1, 1); } +Environment env; +Environment* _lf_environment = &env; + void test_simple() { Main main; - Environment env; Environment_ctor(&env, (Reactor *)&main); Main_ctor(&main, NULL, &env); env.scheduler->duration = MSEC(100); diff --git a/test/unit/reaction_queue_test.c b/test/unit/reaction_queue_test.c index 373310ca..4bbc5412 100644 --- a/test/unit/reaction_queue_test.c +++ b/test/unit/reaction_queue_test.c @@ -1,3 +1,4 @@ +#include "reactor-uc/environment.h" #include "reactor-uc/queues.h" #include "unity.h" @@ -38,6 +39,7 @@ void test_levels_with_gaps(void) { TEST_ASSERT_EQUAL_PTR(r, &rs[i]); } } +Environment * _lf_environment = NULL; int main(void) { UNITY_BEGIN(); diff --git a/test/unit/tcp_channel_test.c b/test/unit/tcp_channel_test.c index dea324c7..7cf7f8f7 100644 --- a/test/unit/tcp_channel_test.c +++ b/test/unit/tcp_channel_test.c @@ -16,6 +16,7 @@ Reactor parent; Environment env; +Environment *_lf_environment = NULL; FederatedConnectionBundle server_bundle; FederatedConnectionBundle client_bundle; FederatedConnectionBundle *net_bundles[] = {&server_bundle, &client_bundle}; diff --git a/test/unit/timer_test.c b/test/unit/timer_test.c index ed6a9bbd..aa1cb54f 100644 --- a/test/unit/timer_test.c +++ b/test/unit/timer_test.c @@ -32,6 +32,7 @@ LF_REACTOR_CTOR_SIGNATURE(TimerTest) { TimerTest my_reactor; Environment env; +Environment* _lf_environment = &env; void test_simple() { Environment_ctor(&env, (Reactor *)&my_reactor); env.scheduler->duration = MSEC(100); From d56ce51bbff288d08f52a1f37ba6e9113d4a40bb Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 16:27:35 +0100 Subject: [PATCH 32/65] TcpIp fixes --- .../platform/posix/tcp_ip_channel.h | 1 - src/federated.c | 2 - src/platform/posix/tcp_ip_channel.c | 38 +------------------ 3 files changed, 1 insertion(+), 40 deletions(-) diff --git a/include/reactor-uc/platform/posix/tcp_ip_channel.h b/include/reactor-uc/platform/posix/tcp_ip_channel.h index b744d8d5..d1565c15 100644 --- a/include/reactor-uc/platform/posix/tcp_ip_channel.h +++ b/include/reactor-uc/platform/posix/tcp_ip_channel.h @@ -26,7 +26,6 @@ struct TcpIpChannel { int fd; int client; int send_failed_event_fds[2]; // These file descriptors are used to signal the recv select to stop blocking - int terminate_event_fds; NetworkChannelState state; pthread_mutex_t mutex; diff --git a/src/federated.c b/src/federated.c index 9452a185..0e0b6ad0 100644 --- a/src/federated.c +++ b/src/federated.c @@ -18,8 +18,6 @@ void FederatedConnectionBundle_connect_to_peers(FederatedConnectionBundle **bund validate(ret == LF_OK); } - LF_INFO(FED, "All connections opened"); - bool all_connected = false; interval_t wait_before_retry = FOREVER; // Initialize to maximum so we can find the lowest requested. while (!all_connected) { diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 5b1b24a9..550ee7f6 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -77,20 +77,6 @@ static lf_ret_t _TcpIpChannel_reset_socket(TcpIpChannel *self) { } } - if (self->terminate_event_fds > 0) { - if (close(self->terminate_event_fds) < 0) { - TCP_IP_CHANNEL_ERR("Error closing terminate event fds=%d", errno); - return LF_ERR; - } - } - - if (self->send_failed_event_fds[0] > 0) { - if (close(self->send_failed_event_fds[0]) < 0) { - TCP_IP_CHANNEL_ERR("Error closing sending failed fds=%d", errno); - return LF_ERR; - } - } - if ((self->fd = socket(self->protocol_family, SOCK_STREAM, 0)) < 0) { TCP_IP_CHANNEL_ERR("Error opening socket errno=%d", errno); return LF_ERR; @@ -106,12 +92,6 @@ static lf_ret_t _TcpIpChannel_reset_socket(TcpIpChannel *self) { return LF_ERR; } - self->terminate_event_fds = eventfd(0, EFD_NONBLOCK); - if (self->terminate_event_fds == -1) { - TCP_IP_CHANNEL_ERR("Failed to initialize event file descriptor"); - return LF_ERR; - } - return LF_OK; } @@ -131,9 +111,6 @@ static void _TcpIpChannel_spawn_worker_thread(TcpIpChannel *self) { throw("pthread_attr_setstack failed"); } - // set terminate to false so the loop runs - self->terminate = false; - res = pthread_create(&self->worker_thread, &self->worker_thread_attr, _TcpIpChannel_worker_thread, self); if (res < 0) { throw("pthread_create failed"); @@ -443,14 +420,9 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { FD_ZERO(&readfds); FD_SET(socket, &readfds); FD_SET(self->send_failed_event_fds[0], &readfds); - FD_SET(self->terminate_event_fds, &readfds); // Determine the maximum file descriptor for select - max_fd = socket; - if (self->send_failed_event_fds[0] > max_fd) - max_fd = self->send_failed_event_fds[0]; - if (self->terminate_event_fds > max_fd) - max_fd = self->terminate_event_fds; + max_fd = (socket > self->send_failed_event_fds[0]) ? socket : self->send_failed_event_fds[0]; // Wait for data or cancel if send_failed externally if (select(max_fd + 1, &readfds, NULL, NULL, NULL) < 0) { @@ -472,10 +444,6 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { } else if (FD_ISSET(self->send_failed_event_fds[0], &readfds)) { TCP_IP_CHANNEL_DEBUG("Select -> cancelled by send_block failure"); _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_LOST_CONNECTION); - } else if (FD_ISSET(self->terminate_event_fds, &readfds)) { - TCP_IP_CHANNEL_DEBUG("Select -> cancelled by terminate event"); - self->terminate = true; - break; } } break; @@ -512,10 +480,6 @@ static void TcpIpChannel_free(NetworkChannel *untyped_self) { TCP_IP_CHANNEL_DEBUG("Stopping worker thread"); err = pthread_cancel(self->worker_thread); - ssize_t bytes_written = eventfd_write(self->terminate_event_fds, 1); - if (bytes_written == -1) { - TCP_IP_CHANNEL_ERR("Failed informing worker thread, that send_blocking failed, errno=%d", errno); - } if (err != 0) { TCP_IP_CHANNEL_ERR("Error canceling worker thread %d", err); From e2176a20ff0f88d0f1ebde4489be4b86c16822a7 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 16:29:49 +0100 Subject: [PATCH 33/65] Revert more tcp stuff --- src/platform/posix/tcp_ip_channel.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 550ee7f6..8b21d4c6 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -449,13 +449,11 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { } break; case NETWORK_CHANNEL_STATE_UNINITIALIZED: - break; case NETWORK_CHANNEL_STATE_CLOSED: - TcpIpChannel_close_connection(untyped_self); - self->terminate = true; break; } } + TCP_IP_CHANNEL_INFO("Worker thread terminates"); return NULL; } From 7b49934edd736bce03ebad111c1222fd37129e76 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 16:57:25 +0100 Subject: [PATCH 34/65] Avoid flooding log when a federate closes a socket --- .../reactor-uc/platform/posix/tcp_ip_channel.h | 1 + src/platform/posix/tcp_ip_channel.c | 18 +++++++++++++----- test/lf/src/FederatedBank.lf | 5 ++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/include/reactor-uc/platform/posix/tcp_ip_channel.h b/include/reactor-uc/platform/posix/tcp_ip_channel.h index d1565c15..d7e3e838 100644 --- a/include/reactor-uc/platform/posix/tcp_ip_channel.h +++ b/include/reactor-uc/platform/posix/tcp_ip_channel.h @@ -41,6 +41,7 @@ struct TcpIpChannel { fd_set set; bool is_server; bool terminate; + bool has_warned_about_connection_failure; // required for callbacks pthread_t worker_thread; diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 8b21d4c6..3e93a9c3 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -163,7 +163,10 @@ static lf_ret_t _TcpIpChannel_try_connect_server(NetworkChannel *untyped_self) { return LF_OK; } else { if (errno == EWOULDBLOCK || errno == EAGAIN) { - TCP_IP_CHANNEL_DEBUG("Accept failed. Try again. errno=%d", errno); + if (!self->has_warned_about_connection_failure) { + TCP_IP_CHANNEL_WARN("Accept failed with errno=%d. Will only print one warning.", errno); + self->has_warned_about_connection_failure = true; + } return LF_OK; } else { TCP_IP_CHANNEL_ERR("Accept failed. errno=%d", errno); @@ -197,7 +200,11 @@ static lf_ret_t _TcpIpChannel_try_connect_client(NetworkChannel *untyped_self) { TCP_IP_CHANNEL_DEBUG("Connection in progress!"); return LF_OK; } else { - TCP_IP_CHANNEL_ERR("Connect to %s:%d failed errno=%d", self->host, self->port, errno); + if (!self->has_warned_about_connection_failure) { + TCP_IP_CHANNEL_WARN("Connect to %s:%d failed with errno=%d. Will only print one error.", self->host, + self->port, errno); + self->has_warned_about_connection_failure = true; + } _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTION_FAILED); return LF_ERR; } @@ -328,7 +335,7 @@ static lf_ret_t _TcpIpChannel_receive(NetworkChannel *untyped_self, FederateMess } else if (bytes_read == 0) { // This means the connection was closed. _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CLOSED); - TCP_IP_CHANNEL_DEBUG("Other federate gracefully closed socket"); + TCP_IP_CHANNEL_WARN("Other federate closed socket"); return LF_ERR; } @@ -439,7 +446,7 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { validate(self->receive_callback); self->receive_callback(self->federated_connection, &self->output); } else if (ret == LF_ERR) { - /* Return to see what the error was by inspecting the network channel state.*/ + _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_LOST_CONNECTION); } } else if (FD_ISSET(self->send_failed_event_fds[0], &readfds)) { TCP_IP_CHANNEL_DEBUG("Select -> cancelled by send_block failure"); @@ -453,7 +460,7 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { break; } } - + TCP_IP_CHANNEL_INFO("Worker thread terminates"); return NULL; } @@ -542,6 +549,7 @@ void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, u self->receive_callback = NULL; self->federated_connection = NULL; self->worker_thread = 0; + self->has_warned_about_connection_failure = false; _TcpIpChannel_reset_socket(self); _TcpIpChannel_spawn_worker_thread(self); diff --git a/test/lf/src/FederatedBank.lf b/test/lf/src/FederatedBank.lf index d30d16db..0f179b47 100644 --- a/test/lf/src/FederatedBank.lf +++ b/test/lf/src/FederatedBank.lf @@ -1,7 +1,7 @@ target uC { platform: Native, timeout: 1 sec, - logging: Debug + logging: Warn } @@ -12,6 +12,9 @@ reactor Src { printf("Hello from Src!\n"); lf_set(out, 42); =} + reaction(shutdown) {= + printf("Src is shutting down\n"); + =} } reactor Fed(bank_idx: int = 0) { From 6b5e91d7eb74cd74669d39a75d70639cccb31329 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 17:00:11 +0100 Subject: [PATCH 35/65] Remove merge mistake --- examples/riot/buildAll.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/riot/buildAll.sh b/examples/riot/buildAll.sh index 405ca200..cdc677fd 100755 --- a/examples/riot/buildAll.sh +++ b/examples/riot/buildAll.sh @@ -21,8 +21,3 @@ for board in "${BOARDS[@]}"; do popd done done - -# Build lf example -pushd hello_lf -run/build.sh -popd \ No newline at end of file From 9e79137b30da76ae479dafb0670ab34ce3615b12 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 17:00:35 +0100 Subject: [PATCH 36/65] Formatting --- include/reactor-uc/environment.h | 3 +-- src/platform/posix/tcp_ip_channel.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/reactor-uc/environment.h b/include/reactor-uc/environment.h index 9e02cc00..ee0e542b 100644 --- a/include/reactor-uc/environment.h +++ b/include/reactor-uc/environment.h @@ -9,7 +9,7 @@ #include "reactor-uc/scheduler.h" typedef struct Environment Environment; -extern Environment* _lf_environment; // NOLINT +extern Environment *_lf_environment; // NOLINT struct Environment { Reactor *main; // The top-level reactor of the program. @@ -61,7 +61,6 @@ struct Environment { void (*request_shutdown)(Environment *self); }; - void Environment_ctor(Environment *self, Reactor *main); void Environment_free(Environment *self); diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 3e93a9c3..b957f16b 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -202,7 +202,7 @@ static lf_ret_t _TcpIpChannel_try_connect_client(NetworkChannel *untyped_self) { } else { if (!self->has_warned_about_connection_failure) { TCP_IP_CHANNEL_WARN("Connect to %s:%d failed with errno=%d. Will only print one error.", self->host, - self->port, errno); + self->port, errno); self->has_warned_about_connection_failure = true; } _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTION_FAILED); From 8d511279f889a2e703cff7d59ca67aa3bca20bff Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 17:26:50 +0100 Subject: [PATCH 37/65] Fix posix federated --- examples/posix/federated/receiver.c | 5 ++-- examples/posix/federated/sender.c | 6 +++-- .../posix/federated_lf/src/HelloFederated.lf | 25 ------------------- include/reactor-uc/macros.h | 6 ++--- 4 files changed, 10 insertions(+), 32 deletions(-) delete mode 100644 examples/posix/federated_lf/src/HelloFederated.lf diff --git a/examples/posix/federated/receiver.c b/examples/posix/federated/receiver.c index 95f18d44..a12f2fff 100644 --- a/examples/posix/federated/receiver.c +++ b/examples/posix/federated/receiver.c @@ -50,7 +50,8 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_ex LF_PORT_REGISTER_EFFECT(self->in, self->r, 1); } -LF_DEFINE_FEDERATED_INPUT_CONNECTION(Receiver, in, msg_t, 5, MSEC(100), false); +LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(Receiver, in, msg_t, 5); +LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(Receiver, in, msg_t, 5, MSEC(100), false); typedef struct { FederatedConnectionBundle super; @@ -80,7 +81,7 @@ LF_REACTOR_CTOR_SIGNATURE(MainRecv) { LF_DEFINE_CHILD_INPUT_ARGS(receiver, in, 1, 1); LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, 1, _receiver_in_args[i]); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Receiver, Sender); - LF_BUNDLE_REGISTER_DOWNSTREAM(Receiver, Sender, receiver, in); + lf_connect_federated_input(&self->Receiver_Sender_bundle.inputs[0]->super, &self->receiver->in[0].super); } LF_ENTRY_POINT_FEDERATED(MainRecv, SEC(1), true, true, 1, false) diff --git a/examples/posix/federated/sender.c b/examples/posix/federated/sender.c index b27e89b7..3b6e962e 100644 --- a/examples/posix/federated/sender.c +++ b/examples/posix/federated/sender.c @@ -59,7 +59,8 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, OutputExternalCtorArgs *out_ex LF_PORT_REGISTER_SOURCE(self->out, self->r, 1); } -LF_DEFINE_FEDERATED_OUTPUT_CONNECTION(Sender, out, msg_t, 1) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(Sender, out, msg_t) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(Sender, out, msg_t) typedef struct { FederatedConnectionBundle super; @@ -94,8 +95,9 @@ LF_REACTOR_CTOR_SIGNATURE(MainSender) { LF_DEFINE_CHILD_OUTPUT_ARGS(sender, out, 1, 1); LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, 1, _sender_out_args[i]); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver); - LF_BUNDLE_REGISTER_UPSTREAM(Sender, Receiver, sender, out); + lf_connect_federated_output(self->Sender_Receiver_bundle.outputs[0], self->sender->out); } + LF_ENTRY_POINT_FEDERATED(MainSender, SEC(1), true, false, 1, true) int main() { diff --git a/examples/posix/federated_lf/src/HelloFederated.lf b/examples/posix/federated_lf/src/HelloFederated.lf deleted file mode 100644 index 09f7ec7c..00000000 --- a/examples/posix/federated_lf/src/HelloFederated.lf +++ /dev/null @@ -1,25 +0,0 @@ -target uC - -reactor Source { - output out: char[12] - - timer t(0, 1 sec) - - reaction(t) -> out {= - lf_set_array(out, "Hello World"); - =} -} - -reactor Sink { - input in: char[12] - - reaction(in) {= - printf("Received: %s\n" , in->value); - =} -} - -federated reactor { - src = new Source() - sink = new Sink() - src.out -> sink.in -} \ No newline at end of file diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index 775b23d9..08c863fa 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -607,16 +607,16 @@ typedef struct FederatedInputConnection FederatedInputConnection; BufferType payload_buf[(BufferSize)]; \ bool payload_used_buf[(BufferSize)]; \ Port *downstreams[1]; \ - } ReactorName##_##InputName; + } ReactorName##_##InputName##_conn; #define LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(ReactorName, InputName, BufferType, BufferSize, Delay, IsPhysical) \ - void ReactorName##_##InputName##_conn_ctor(ReactorName##_##InputName *self, Reactor *parent) { \ + void ReactorName##_##InputName##_conn_ctor(ReactorName##_##InputName##_conn *self, Reactor *parent) { \ FederatedInputConnection_ctor(&self->super, parent, Delay, IsPhysical, (Port **)&self->downstreams, 1, \ (void *)&self->payload_buf, (bool *)&self->payload_used_buf, sizeof(BufferType), \ BufferSize); \ } -#define LF_FEDERATED_INPUT_CONNECTION_INSTANCE(ReactorName, InputName) ReactorName##_##InputName InputName +#define LF_FEDERATED_INPUT_CONNECTION_INSTANCE(ReactorName, InputName) ReactorName##_##InputName##_conn InputName #define LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(ReactorName, InputName, DeserializeFunc) \ ReactorName##_##InputName##_conn_ctor(&self->InputName, self->super.parent); \ From b9060048a73ebf65cc4c603dc8fad37216918826 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 18:18:28 +0100 Subject: [PATCH 38/65] More minor fixes --- examples/riot/coap_federated/receiver/main.c | 7 +-- examples/riot/coap_federated/sender/main.c | 7 +-- .../zephyr/basic_federated/common/receiver.h | 5 ++- .../federated_sender/src/sender.c | 7 +-- include/reactor-uc/macros.h | 44 ------------------- test/unit/delayed_conn_test.c | 3 +- test/unit/port_test.c | 3 +- 7 files changed, 17 insertions(+), 59 deletions(-) diff --git a/examples/riot/coap_federated/receiver/main.c b/examples/riot/coap_federated/receiver/main.c index 9c50219c..65978cdd 100755 --- a/examples/riot/coap_federated/receiver/main.c +++ b/examples/riot/coap_federated/receiver/main.c @@ -53,14 +53,15 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_ex LF_PORT_REGISTER_EFFECT(self->in, self->r, 1); } -LF_DEFINE_FEDERATED_INPUT_CONNECTION(Receiver, in, lf_msg_t, 5, MSEC(100), false) +LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(Receiver, in, msg_t, 5); +LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(Receiver, in, msg_t, 5, MSEC(100), false); typedef struct { FederatedConnectionBundle super; CoapUdpIpChannel channel; LF_FEDERATED_INPUT_CONNECTION_INSTANCE(Receiver, in); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(1, 0) -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Receiver, Sender); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Receiver, Sender); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Receiver, Sender) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); @@ -83,7 +84,7 @@ LF_REACTOR_CTOR_SIGNATURE(MainRecv) { LF_DEFINE_CHILD_INPUT_ARGS(receiver, in, 1, 1); LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, 1, _receiver_in_args[i]); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Receiver, Sender); - LF_BUNDLE_REGISTER_DOWNSTREAM(Receiver, Sender, receiver, in); + lf_connect_federated_input(&self->Receiver_Sender_bundle.inputs[0]->super, &self->receiver->in[0].super); } LF_ENTRY_POINT_FEDERATED(MainRecv, SEC(1), true, true, 1, false) diff --git a/examples/riot/coap_federated/sender/main.c b/examples/riot/coap_federated/sender/main.c index 9a0e47b3..b6d6ab79 100755 --- a/examples/riot/coap_federated/sender/main.c +++ b/examples/riot/coap_federated/sender/main.c @@ -60,14 +60,15 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, OutputExternalCtorArgs *out_ex LF_PORT_REGISTER_SOURCE(self->out, self->r, 1); } -LF_DEFINE_FEDERATED_OUTPUT_CONNECTION(Sender, out, lf_msg_t, 1) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(Sender, out, msg_t) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(Sender, out, msg_t) typedef struct { FederatedConnectionBundle super; CoapUdpIpChannel channel; LF_FEDERATED_OUTPUT_CONNECTION_INSTANCE(Sender, out); LF_FEDERATED_CONNECTION_BUNDLE_BOOKKEEPING_INSTANCES(0, 1); -} LF_FEDERATED_CONNECTION_BUNDLE_NAME(Sender, Receiver); +} LF_FEDERATED_CONNECTION_BUNDLE_TYPE(Sender, Receiver); LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); @@ -93,7 +94,7 @@ LF_REACTOR_CTOR_SIGNATURE(MainSender) { LF_DEFINE_CHILD_OUTPUT_ARGS(sender, out, 1, 1); LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, 1, _sender_out_args[i]); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver); - LF_BUNDLE_REGISTER_UPSTREAM(Sender, Receiver, sender, out); + lf_connect_federated_output((Connection *)self->Sender_Receiver_bundle.outputs[0], (Port *)self->sender->out); } LF_ENTRY_POINT_FEDERATED(MainSender, SEC(1), true, false, 1, true) diff --git a/examples/zephyr/basic_federated/common/receiver.h b/examples/zephyr/basic_federated/common/receiver.h index ff7c2d63..37fd1c59 100644 --- a/examples/zephyr/basic_federated/common/receiver.h +++ b/examples/zephyr/basic_federated/common/receiver.h @@ -64,7 +64,8 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_ex LF_PORT_REGISTER_EFFECT(self->in, self->r, 1); } -LF_DEFINE_FEDERATED_INPUT_CONNECTION(Receiver, in, msg_t, 5, MSEC(100), false); +LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(Receiver, in, msg_t, 5); +LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(Receiver, in, msg_t, 5, MSEC(100), false); typedef struct { FederatedConnectionBundle super; @@ -94,6 +95,6 @@ LF_REACTOR_CTOR_SIGNATURE(MainRecv) { LF_DEFINE_CHILD_INPUT_ARGS(receiver, in, 1, 1); LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, 1, _receiver_in_args[i]); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Receiver, Sender); - LF_BUNDLE_REGISTER_DOWNSTREAM(Receiver, Sender, receiver, in); + lf_connect_federated_input(&self->Receiver_Sender_bundle.inputs[0]->super, &self->receiver->in[0].super); } LF_ENTRY_POINT_FEDERATED(MainRecv, FOREVER, true, true, 1, false) diff --git a/examples/zephyr/basic_federated/federated_sender/src/sender.c b/examples/zephyr/basic_federated/federated_sender/src/sender.c index ee6e9566..9f55d6b7 100644 --- a/examples/zephyr/basic_federated/federated_sender/src/sender.c +++ b/examples/zephyr/basic_federated/federated_sender/src/sender.c @@ -97,7 +97,8 @@ LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, OutputExternalCtorArgs *out_ex LF_PORT_REGISTER_SOURCE(self->out, self->r, 1); } -LF_DEFINE_FEDERATED_OUTPUT_CONNECTION(Sender, out, msg_t, 1) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(Sender, out, msg_t) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(Sender, out, msg_t) typedef struct { FederatedConnectionBundle super; @@ -152,8 +153,8 @@ LF_REACTOR_CTOR_SIGNATURE(MainSender) { LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver1); LF_INITIALIZE_FEDERATED_CONNECTION_BUNDLE(Sender, Receiver2); - LF_BUNDLE_REGISTER_UPSTREAM(Sender, Receiver1, sender, out); - LF_BUNDLE_REGISTER_UPSTREAM(Sender, Receiver2, sender, out); + lf_connect_federated_output(self->Sender_Receiver1_bundle.outputs[0], self->sender->out); + lf_connect_federated_output(self->Sender_Receiver2_bundle.outputs[0], self->sender->out); } LF_ENTRY_POINT_FEDERATED(MainSender, FOREVER, true, true, 2, true) diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index 08c863fa..399b5b3b 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -137,52 +137,8 @@ (__reaction)->effects[(__reaction)->effects_registered++] = (Trigger *)&(TheEffect); \ } while (0) -// Convenience macro to register a downstream port on a connection. -#define LF_CONN_REGISTER_DOWNSTREAM_INTERNAL(conn, down) \ - do { \ - ((Connection *)&(conn))->register_downstream((Connection *)&(conn), (Port *)&(down)); \ - } while (0) - -// Convenience macro to register an upstream port on a connection -#define LF_CONN_REGISTER_UPSTREAM_INTERNAL(conn, up) \ - do { \ - Port *_up = (Port *)&(up); \ - ((Connection *)&(conn))->upstream = _up; \ - assert(_up->conns_out_registered < _up->conns_out_size); \ - _up->conns_out[_up->conns_out_registered++] = (Connection *)&(conn); \ - } while (0) - -#define LF_BUNDLE_REGISTER_DOWNSTREAM(ReactorName, OtherName, InstanceName, Port) \ - LF_CONN_REGISTER_DOWNSTREAM_INTERNAL(self->ReactorName##_##OtherName##_bundle.conn_##Port, self->InstanceName->Port); - -#define LF_BUNDLE_REGISTER_UPSTREAM(ReactorName, OtherName, InstanceName, Port) \ - LF_CONN_REGISTER_UPSTREAM_INTERNAL(self->ReactorName##_##OtherName##_bundle.conn_##Port, self->InstanceName->Port); - -#define LF_CONN_REGISTER_UPSTREAM(Conn, ReactorUp, PortUp, BankWidth, PortWidth) \ - for (int i = 0; i < (BankWidth); i++) { \ - for (int j = 0; j < (PortWidth); j++) { \ - LF_CONN_REGISTER_UPSTREAM_INTERNAL(self->Conn[i][j], ReactorUp[i].PortUp[j]); \ - } \ - } - -#define LF_CONN_REGISTER_DOWNSTREAM(Conn, BankWidthUp, PortWidthUp, ReactorDown, PortDown, BankWidthDown, \ - PortWidthDown) \ - for (int i = 0; i < (BankWidthDown); i++) { \ - for (int j = 0; j < (PortWidthDown); j++) { \ - LF_CONN_REGISTER_DOWNSTREAM_INTERNAL(self->Conn[_##Conn##_i][_##Conn##_j], ReactorDown[i].PortDown[j]); \ - _##Conn##_j++; \ - if (_##Conn##_j == (PortWidthUp)) { \ - _##Conn##_j = 0; \ - _##Conn##_i++; \ - } \ - if (_##Conn##_i == (BankWidthUp)) { \ - _##Conn##_i = 0; \ - } \ - } \ - } // Macros for creating the structs and ctors - #define LF_REACTOR_CTOR_PREAMBLE() \ size_t _reactions_idx = 0; \ (void)_reactions_idx; \ diff --git a/test/unit/delayed_conn_test.c b/test/unit/delayed_conn_test.c index adea5ec7..d411e4b2 100644 --- a/test/unit/delayed_conn_test.c +++ b/test/unit/delayed_conn_test.c @@ -98,8 +98,7 @@ LF_REACTOR_CTOR_SIGNATURE(Main) { LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, 1, _receiver_in_args); LF_INITIALIZE_DELAYED_CONNECTION(Main, sender_out, 1, 1); - LF_CONN_REGISTER_UPSTREAM(sender_out, self->sender, out, 1, 1); - LF_CONN_REGISTER_DOWNSTREAM(sender_out, 1,1, self->receiver, in, 1, 1); + lf_connect(&self->sender_out[0][0].super, &self->sender->out[0].super, &self->receiver->in[0].super); } Environment env; diff --git a/test/unit/port_test.c b/test/unit/port_test.c index f90f98b9..f48bb5be 100644 --- a/test/unit/port_test.c +++ b/test/unit/port_test.c @@ -97,8 +97,7 @@ LF_REACTOR_CTOR_SIGNATURE(Main) { LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver,1, &_receiver_in_args[0]); LF_INITIALIZE_LOGICAL_CONNECTION(Main, sender_out, 1, 1); - LF_CONN_REGISTER_UPSTREAM(sender_out, self->sender, out, 1, 1); - LF_CONN_REGISTER_DOWNSTREAM(sender_out, 1,1,self->receiver, in, 1, 1); + lf_connect(&self->sender_out[0][0].super, &self->sender->out[0].super, &self->receiver->in[0].super); } Environment env; From ef9f9800b1280b9dbffdc8187e940987b222eea2 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 18:20:30 +0100 Subject: [PATCH 39/65] Format --- include/reactor-uc/macros.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index 399b5b3b..7be5f261 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -137,7 +137,6 @@ (__reaction)->effects[(__reaction)->effects_registered++] = (Trigger *)&(TheEffect); \ } while (0) - // Macros for creating the structs and ctors #define LF_REACTOR_CTOR_PREAMBLE() \ size_t _reactions_idx = 0; \ @@ -566,7 +565,7 @@ typedef struct FederatedInputConnection FederatedInputConnection; } ReactorName##_##InputName##_conn; #define LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(ReactorName, InputName, BufferType, BufferSize, Delay, IsPhysical) \ - void ReactorName##_##InputName##_conn_ctor(ReactorName##_##InputName##_conn *self, Reactor *parent) { \ + void ReactorName##_##InputName##_conn_ctor(ReactorName##_##InputName##_conn *self, Reactor *parent) { \ FederatedInputConnection_ctor(&self->super, parent, Delay, IsPhysical, (Port **)&self->downstreams, 1, \ (void *)&self->payload_buf, (bool *)&self->payload_used_buf, sizeof(BufferType), \ BufferSize); \ From 05d7d8736af3c9b39f881061549e3accb906702e Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 19:05:56 +0100 Subject: [PATCH 40/65] Minimum event queue of 2 --- .../lflang/generator/uc/UcCmakeGenerator.kt | 2 +- .../lflang/generator/uc/UcMakeGenerator.kt | 30 ++++++++++++------- .../uc/UcPlatformGeneratorFederated.kt | 2 +- test/platform/riot/coap_channel_test/main.c | 1 + 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt index 8b5c30d7..7f4f5f1e 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcCmakeGenerator.kt @@ -38,7 +38,7 @@ abstract class UcCmakeGenerator(private val targetConfig: TargetConfig, private |set(REACTOR_UC_PATH $S{CMAKE_CURRENT_LIST_DIR}/reactor-uc) |set(LFC_GEN_INCLUDE_DIRS $S{CMAKE_CURRENT_LIST_DIR}) |set(REACTION_QUEUE_SIZE ${max(numReactions, 1)} CACHE STRING "Size of the reaction queue") - |set(EVENT_QUEUE_SIZE ${max(numEvents, 1)} CACHE STRING "Size of the event queue") + |set(EVENT_QUEUE_SIZE ${max(numEvents, 2)} CACHE STRING "Size of the event queue") """.trimMargin() } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt index abb6d354..95509554 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMakeGenerator.kt @@ -10,22 +10,32 @@ import java.nio.file.Path import kotlin.io.path.name import kotlin.math.max -abstract class UcMakeGenerator() { +abstract class UcMakeGenerator(private val mainTarget: String, private val numEvents: Int, private val numReactions: Int) { abstract fun generateMake(sources: List): String -} - -class UcMakeGeneratorNonFederated(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig, private val numEvents: Int, private val numReactions: Int): UcMakeGenerator() { - private val S = '$' // a little trick to escape the dollar sign with $S - override fun generateMake(sources: List) = with(PrependOperator) { - val sources = sources.filterNot { it.name=="lf_main.c" } + protected val S = '$' // a little trick to escape the dollar sign with $S + fun doGenerateMake(sources: List, compileDefs: List) = with(PrependOperator) { + val sources = sources.filterNot { it.name == "lf_main.c" } """ - | # Makefile generated for ${fileConfig.name} + | # Makefile generated for ${mainTarget} |LFC_GEN_SOURCES = \ - ${" | "..sources.joinWithLn { it.toUnixString() + if (it != sources.last()) " \\" else ""}} + ${" | "..sources.joinWithLn { it.toUnixString() + if (it != sources.last()) " \\" else "" }} |LFC_GEN_MAIN = lf_main.c + |LFC_GEN_COMPILE_DEFS = \ + ${" | "..compileDefs.joinWithLn { it + if (it != compileDefs.last()) " \\" else "" }} |REACTION_QUEUE_SIZE = ${max(numReactions, 1)} - |EVENT_QUEUE_SIZE = ${max(numEvents, 1)} + |EVENT_QUEUE_SIZE = ${max(numEvents, 2)} | """.trimMargin() } } + +class UcMakeGeneratorNonFederated(private val main: Reactor, private val targetConfig: TargetConfig, private val fileConfig: FileConfig, numEvents: Int, numReactions: Int) + : UcMakeGenerator(fileConfig.name, numEvents, numReactions) { + override fun generateMake(sources: List) = doGenerateMake(sources, emptyList()) + +} + +class UcMakeGeneratorFederated(private val federate: UcFederate, targetConfig: TargetConfig, fileConfig: UcFileConfig, numEvents: Int, numReactions: Int) + : UcMakeGenerator(federate.codeType, numEvents, numReactions) { + override fun generateMake(sources: List) = doGenerateMake(sources, federate.getCompileDefs()) +} diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt index 1c611820..b9db2c7c 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt @@ -15,7 +15,7 @@ class UcPlatformGeneratorFederated(generator: UcGeneratorFederated, override val val mainGenerator = UcMainGeneratorFederated(federate, generator.federates, generator.targetConfig, generator.fileConfig) val numEventsAndReactions = generator.totalNumEventsAndReactionsFederated(federate) val cmakeGenerator = UcCmakeGeneratorFederated(federate, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) - val makeGenerator = UcMakeGeneratorNonFederated(federate.inst.reactor, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) + val makeGenerator = UcMakeGeneratorFederated(federate, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) super.doGeneratePlatformFiles(mainGenerator, cmakeGenerator, makeGenerator) generateLaunchScript() diff --git a/test/platform/riot/coap_channel_test/main.c b/test/platform/riot/coap_channel_test/main.c index 310da758..5b7a5a4a 100644 --- a/test/platform/riot/coap_channel_test/main.c +++ b/test/platform/riot/coap_channel_test/main.c @@ -14,6 +14,7 @@ Reactor parent; Environment env; +Environment *_lf_environment = &env; FederatedConnectionBundle bundle; FederatedConnectionBundle *net_bundles[] = {&bundle}; From 42166cd7dc84afc4b9448b9cf4d6f02b50d9c9af Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 15 Jan 2025 19:11:30 +0100 Subject: [PATCH 41/65] Remove some dead code --- include/reactor-uc/macros.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/reactor-uc/macros.h b/include/reactor-uc/macros.h index 7be5f261..5eba466d 100644 --- a/include/reactor-uc/macros.h +++ b/include/reactor-uc/macros.h @@ -469,8 +469,6 @@ } #define LF_INITIALIZE_LOGICAL_CONNECTION(ParentName, ConnName, BankWidth, PortWidth) \ - int _##ConnName##_i = 0; \ - int _##ConnName##_j = 0; \ for (int i = 0; i < (BankWidth); i++) { \ for (int j = 0; j < (PortWidth); j++) { \ ParentName##_##ConnName##_ctor(&self->ConnName[i][j], &self->super); \ @@ -497,8 +495,6 @@ // FIXME: Duplicated #define LF_INITIALIZE_DELAYED_CONNECTION(ParentName, ConnName, BankWidth, PortWidth) \ - int _##ConnName##_i = 0; \ - int _##ConnName##_j = 0; \ for (int i = 0; i < (BankWidth); i++) { \ for (int j = 0; j < (PortWidth); j++) { \ ParentName##_##ConnName##_ctor(&self->ConnName[i][j], &self->super); \ From 7d435269f40790d1b423a93bd8482964f6252cae Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 16 Jan 2025 11:07:19 +0100 Subject: [PATCH 42/65] Also close send_failed socketpair on reset --- src/platform/posix/tcp_ip_channel.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index b957f16b..671934cf 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -76,6 +76,18 @@ static lf_ret_t _TcpIpChannel_reset_socket(TcpIpChannel *self) { return LF_ERR; } } + if (self->send_failed_event_fds[0] > 0) { + if (close(self->send_failed_event_fds[0]) < 0) { + TCP_IP_CHANNEL_ERR("Error closing send_failed_event_fds[0] errno=%d", errno); + return LF_ERR; + } + } + if (self->send_failed_event_fds[1] > 0) { + if (close(self->send_failed_event_fds[1]) < 0) { + TCP_IP_CHANNEL_ERR("Error closing send_failed_event_fds[1] errno=%d", errno); + return LF_ERR; + } + } if ((self->fd = socket(self->protocol_family, SOCK_STREAM, 0)) < 0) { TCP_IP_CHANNEL_ERR("Error opening socket errno=%d", errno); From b11b5b8b7572d1c54efe0d22ead368a18527ab7b Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 16 Jan 2025 11:07:30 +0100 Subject: [PATCH 43/65] Generate return 0 in main function --- .../src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt index 51e1ee50..462f2869 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcMainGenerator.kt @@ -32,6 +32,7 @@ abstract class UcMainGenerator(val targetConfig: TargetConfig) { |#include "lf_start.h" |int main(void) { | lf_start(); + | return 0; |} """.trimMargin() } From c3d1d743b81fee4e21900b54d2114f83a25196b6 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Thu, 16 Jan 2025 11:08:00 +0100 Subject: [PATCH 44/65] Only generate launch script when we target native --- .../org/lflang/generator/uc/UcPlatformGeneratorFederated.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt index b9db2c7c..e8810314 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcPlatformGeneratorFederated.kt @@ -1,6 +1,8 @@ package org.lflang.generator.uc import org.lflang.reactor +import org.lflang.target.property.PlatformProperty +import org.lflang.target.property.type.PlatformType import org.lflang.util.FileUtil import java.nio.file.Path @@ -18,7 +20,9 @@ class UcPlatformGeneratorFederated(generator: UcGeneratorFederated, override val val makeGenerator = UcMakeGeneratorFederated(federate, targetConfig, generator.fileConfig, numEventsAndReactions.first, numEventsAndReactions.second) super.doGeneratePlatformFiles(mainGenerator, cmakeGenerator, makeGenerator) - generateLaunchScript() + if (targetConfig.get(PlatformProperty.INSTANCE).platform == PlatformType.Platform.NATIVE) { + generateLaunchScript() + } } fun generateLaunchScript() { From 66c6cc9db3d6dad5387d8f173bd91435b858de8b Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 17 Jan 2025 15:19:35 +0100 Subject: [PATCH 45/65] Set timeout of 1minute on our LF tests --- test/lf/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/lf/Makefile b/test/lf/Makefile index 54f98004..8766b49e 100644 --- a/test/lf/Makefile +++ b/test/lf/Makefile @@ -1,6 +1,7 @@ # Very simple Makefile script to build and compile all the LF tests. SRCS = $(wildcard src/*.lf) BINS = $(patsubst src/%.lf, bin/%, $(SRCS)) +TIMEOUT_S = 60 SRCS_ONLY_BUILD = $(wildcard src/only_build/*.lf) BINS_ONLY_BUILD = $(patsubst src/only_build/%.lf, bin/%, $(SRCS)) @@ -16,7 +17,7 @@ build_lfc: bin/%: src/%.lf ${LFC} $^ -c - ./$@ + timeout ${TIMEOUT_S}s ./$@ bin/%: src/only_build/%.lf ${LFC} $^ -c From 2b7fb24a139405f605814141813b1ab0b3eaef6d Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 17 Jan 2025 15:42:24 +0100 Subject: [PATCH 46/65] Add some info prints --- src/platform/posix/tcp_ip_channel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 671934cf..5ca4366a 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -171,6 +171,7 @@ static lf_ret_t _TcpIpChannel_try_connect_server(NetworkChannel *untyped_self) { if (new_socket >= 0) { self->client = new_socket; FD_SET(new_socket, &self->set); + TCP_IP_CHANNEL_INFO("Connceted to client with address %s", inet_ntoa(address.sin_addr)); _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTED); return LF_OK; } else { @@ -204,6 +205,7 @@ static lf_ret_t _TcpIpChannel_try_connect_client(NetworkChannel *untyped_self) { int ret = connect(self->fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); if (ret == 0) { + TCP_IP_CHANNEL_INFO("Connected to server on %s:%d", self->host, self->port); _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTED); return LF_OK; } else { From c9e949f2ba7b3451b50c0f61f37e444fcf26f5d6 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 17 Jan 2025 15:54:10 +0100 Subject: [PATCH 47/65] Add was_ever_connected API to network_channel --- include/reactor-uc/network_channel.h | 7 +++++++ include/reactor-uc/platform/posix/tcp_ip_channel.h | 1 + .../reactor-uc/platform/riot/coap_udp_ip_channel.h | 1 + src/federated.c | 2 +- src/platform/posix/tcp_ip_channel.c | 11 +++++++++++ src/platform/riot/coap_udp_ip_channel.c | 11 +++++++++++ 6 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/reactor-uc/network_channel.h b/include/reactor-uc/network_channel.h index 918ef27b..51e82321 100644 --- a/include/reactor-uc/network_channel.h +++ b/include/reactor-uc/network_channel.h @@ -55,6 +55,13 @@ struct NetworkChannel { */ bool (*is_connected)(NetworkChannel *self); + /** + * @brief Has the network channel ever been connected to its peer? + * This is needed because we currently require an initial connection + * to be established to all peers before a federate can start. + */ + bool (*was_ever_connected)(NetworkChannel *self); + /** * @brief Opens the connection to the corresponding NetworkChannel on another federate (non-blocking). * The channel is not connected unless @p is_connected returns true. diff --git a/include/reactor-uc/platform/posix/tcp_ip_channel.h b/include/reactor-uc/platform/posix/tcp_ip_channel.h index d7e3e838..13eb4e0e 100644 --- a/include/reactor-uc/platform/posix/tcp_ip_channel.h +++ b/include/reactor-uc/platform/posix/tcp_ip_channel.h @@ -42,6 +42,7 @@ struct TcpIpChannel { bool is_server; bool terminate; bool has_warned_about_connection_failure; + bool was_ever_connected; // required for callbacks pthread_t worker_thread; diff --git a/include/reactor-uc/platform/riot/coap_udp_ip_channel.h b/include/reactor-uc/platform/riot/coap_udp_ip_channel.h index 0c5948ff..f1c33098 100644 --- a/include/reactor-uc/platform/riot/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/riot/coap_udp_ip_channel.h @@ -23,6 +23,7 @@ struct CoapUdpIpChannel { sock_udp_ep_t remote; bool send_ack_received; + bool was_ever_connected; FederateMessage output; uint8_t write_buffer[COAP_UDP_IP_CHANNEL_BUFFERSIZE]; diff --git a/src/federated.c b/src/federated.c index 0e0b6ad0..5d4fb861 100644 --- a/src/federated.c +++ b/src/federated.c @@ -25,7 +25,7 @@ void FederatedConnectionBundle_connect_to_peers(FederatedConnectionBundle **bund for (size_t i = 0; i < bundles_size; i++) { FederatedConnectionBundle *bundle = bundles[i]; NetworkChannel *chan = bundle->net_channel; - if (!chan->is_connected(chan)) { + if (!chan->was_ever_connected(chan)) { if (chan->expected_connect_duration < wait_before_retry && chan->expected_connect_duration > 0) { wait_before_retry = chan->expected_connect_duration; } diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 5ca4366a..38838a83 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -49,6 +49,10 @@ static void _TcpIpChannel_update_state(TcpIpChannel *self, NetworkChannelState n self->state = new_state; pthread_mutex_unlock(&self->mutex); + if (new_state == NETWORK_CHANNEL_STATE_CONNECTED) { + self->was_ever_connected = true; + } + // Inform runtime about new state if it changed from or to NETWORK_CHANNEL_STATE_CONNECTED if ((old_state == NETWORK_CHANNEL_STATE_CONNECTED && new_state != NETWORK_CHANNEL_STATE_CONNECTED) || (old_state != NETWORK_CHANNEL_STATE_CONNECTED && new_state == NETWORK_CHANNEL_STATE_CONNECTED)) { @@ -524,6 +528,11 @@ static bool TcpIpChannel_is_connected(NetworkChannel *untyped_self) { return _TcpIpChannel_get_state(self) == NETWORK_CHANNEL_STATE_CONNECTED; } +static bool TcpIpChannel_was_ever_connected(NetworkChannel *untyped_self) { + TcpIpChannel *self = (TcpIpChannel *)untyped_self; + return self->was_ever_connected; +} + void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, unsigned short port, int protocol_family, bool is_server) { assert(self != NULL); @@ -553,6 +562,7 @@ void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, u self->state = NETWORK_CHANNEL_STATE_UNINITIALIZED; self->super.is_connected = TcpIpChannel_is_connected; + self->super.was_ever_connected = TcpIpChannel_was_ever_connected; self->super.open_connection = TcpIpChannel_open_connection; self->super.close_connection = TcpIpChannel_close_connection; self->super.send_blocking = TcpIpChannel_send_blocking; @@ -564,6 +574,7 @@ void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, u self->federated_connection = NULL; self->worker_thread = 0; self->has_warned_about_connection_failure = false; + self->was_ever_connected = false; _TcpIpChannel_reset_socket(self); _TcpIpChannel_spawn_worker_thread(self); diff --git a/src/platform/riot/coap_udp_ip_channel.c b/src/platform/riot/coap_udp_ip_channel.c index 758cab37..4b0afde6 100644 --- a/src/platform/riot/coap_udp_ip_channel.c +++ b/src/platform/riot/coap_udp_ip_channel.c @@ -30,6 +30,10 @@ static void _CoapUdpIpChannel_update_state(CoapUdpIpChannel *self, NetworkChanne self->state = new_state; mutex_unlock(&self->state_mutex); + if (new_state == NETWORK_CHANNEL_STATE_CONNECTED) { + self->was_ever_connected = true; + } + // Inform runtime about new state if it changed from or to NETWORK_CHANNEL_STATE_CONNECTED if ((old_state == NETWORK_CHANNEL_STATE_CONNECTED && new_state != NETWORK_CHANNEL_STATE_CONNECTED) || (old_state != NETWORK_CHANNEL_STATE_CONNECTED && new_state == NETWORK_CHANNEL_STATE_CONNECTED)) { @@ -357,6 +361,11 @@ static bool CoapUdpIpChannel_is_connected(NetworkChannel *untyped_self) { return _CoapUdpIpChannel_get_state(self) == NETWORK_CHANNEL_STATE_CONNECTED; } +static bool CoapUdpIpChannel_was_ever_connected(NetworkChannel *untyped_self) { + CoapUdpIpChannel *self = (CoapUdpIpChannel *)untyped_self; + return self->was_ever_connected; +} + void *_CoapUdpIpChannel_connection_thread(void *arg) { COAP_UDP_IP_CHANNEL_DEBUG("Start connection thread"); (void)arg; @@ -421,6 +430,7 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, Environment *env, const char self->super.expected_connect_duration = COAP_UDP_IP_CHANNEL_EXPECTED_CONNECT_DURATION; self->super.type = NETWORK_CHANNEL_TYPE_COAP_UDP_IP; self->super.is_connected = CoapUdpIpChannel_is_connected; + self->super.was_ever_connected = CoapUdpIpChannel_was_ever_connected; self->super.open_connection = CoapUdpIpChannel_open_connection; self->super.close_connection = CoapUdpIpChannel_close_connection; self->super.send_blocking = CoapUdpIpChannel_send_blocking; @@ -432,6 +442,7 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, Environment *env, const char self->federated_connection = NULL; self->state = NETWORK_CHANNEL_STATE_UNINITIALIZED; self->state_mutex = (mutex_t)MUTEX_INIT; + self->was_ever_connected = false; // Convert host to udp socket if (inet_pton(remote_protocol_family, remote_address, self->remote.addr.ipv6) == 1) { From fac8be9e6c010267524341cedbcd4ab934b93785 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 17 Jan 2025 15:54:16 +0100 Subject: [PATCH 48/65] Fix some warnings in unit-tests --- Makefile | 2 +- test/unit/delayed_conn_test.c | 6 +++--- test/unit/port_test.c | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index cfda6301..72bb4acb 100644 --- a/Makefile +++ b/Makefile @@ -49,4 +49,4 @@ format-check: ci: clean test coverage format-check clean: - rm -rf build + rm -rf build test/lf/src-gen test/lf/bin diff --git a/test/unit/delayed_conn_test.c b/test/unit/delayed_conn_test.c index d411e4b2..05d74014 100644 --- a/test/unit/delayed_conn_test.c +++ b/test/unit/delayed_conn_test.c @@ -93,12 +93,12 @@ LF_REACTOR_CTOR_SIGNATURE(Main) { LF_REACTOR_CTOR(Main); LF_DEFINE_CHILD_OUTPUT_ARGS(sender, out, 1, 1); - LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, 1, _sender_out_args); + LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender, 1, &_sender_out_args[0][0]); LF_DEFINE_CHILD_INPUT_ARGS(receiver, in, 1, 1); - LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, 1, _receiver_in_args); + LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver, 1, &_receiver_in_args[0][0]); LF_INITIALIZE_DELAYED_CONNECTION(Main, sender_out, 1, 1); - lf_connect(&self->sender_out[0][0].super, &self->sender->out[0].super, &self->receiver->in[0].super); + lf_connect(&self->sender_out[0][0].super.super, &self->sender->out[0].super, &self->receiver->in[0].super); } Environment env; diff --git a/test/unit/port_test.c b/test/unit/port_test.c index f48bb5be..efd7547e 100644 --- a/test/unit/port_test.c +++ b/test/unit/port_test.c @@ -92,12 +92,12 @@ LF_REACTOR_CTOR_SIGNATURE(Main) { LF_REACTOR_CTOR(Main); LF_DEFINE_CHILD_OUTPUT_ARGS(sender, out,1,1); - LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender,1, &_sender_out_args[0]); + LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Sender, sender,1, &_sender_out_args[0][0]); LF_DEFINE_CHILD_INPUT_ARGS(receiver, in,1,1); - LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver,1, &_receiver_in_args[0]); + LF_INITIALIZE_CHILD_REACTOR_WITH_PARAMETERS(Receiver, receiver,1, &_receiver_in_args[0][0]); LF_INITIALIZE_LOGICAL_CONNECTION(Main, sender_out, 1, 1); - lf_connect(&self->sender_out[0][0].super, &self->sender->out[0].super, &self->receiver->in[0].super); + lf_connect(&self->sender_out[0][0].super.super, &self->sender->out[0].super, &self->receiver->in[0].super); } Environment env; From 07c23a961c00334c37a320dff6674b8cb97bd59d Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 17 Jan 2025 16:03:29 +0100 Subject: [PATCH 49/65] Avoid some unnecessary LF_INFO calls --- src/environment.c | 2 +- src/federated.c | 4 ++-- src/reaction.c | 2 +- src/schedulers/dynamic/scheduler.c | 4 ++-- test/lf/src/FederatedBank.lf | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/environment.c b/src/environment.c index 99109974..f4d98a23 100644 --- a/src/environment.c +++ b/src/environment.c @@ -93,7 +93,7 @@ void Environment_ctor(Environment *self, Reactor *main) { void Environment_free(Environment *self) { (void)self; - LF_INFO(ENV, "Freeing environment"); + LF_INFO(ENV, "Reactor shutting down, freeing environment."); for (size_t i = 0; i < self->net_bundles_size; i++) { NetworkChannel *chan = self->net_bundles[i]->net_channel; chan->free(chan); diff --git a/src/federated.c b/src/federated.c index 5d4fb861..4dd5a647 100644 --- a/src/federated.c +++ b/src/federated.c @@ -7,7 +7,7 @@ void LogicalConnection_trigger_downstreams(Connection *self, const void *value, size_t value_size); void FederatedConnectionBundle_connect_to_peers(FederatedConnectionBundle **bundles, size_t bundles_size) { - LF_INFO(FED, "Connecting to %zu federated peers", bundles_size); + LF_INFO(FED, "%s connecting to %zu federated peers", _lf_environment->main->name, bundles_size); lf_ret_t ret; Environment *env = bundles[0]->parent->env; @@ -37,7 +37,7 @@ void FederatedConnectionBundle_connect_to_peers(FederatedConnectionBundle **bund } } - LF_DEBUG(FED, "Established connection to all %zu federated peers", bundles_size); + LF_INFO(FED, "%s Established connection to all %zu federated peers", _lf_environment->main->name, bundles_size); } // Called when a reaction does lf_set(outputPort). Should buffer the output data diff --git a/src/reaction.c b/src/reaction.c index c27536c0..43f36ede 100644 --- a/src/reaction.c +++ b/src/reaction.c @@ -33,7 +33,7 @@ int calculate_port_level(Port *port) { } } - LF_INFO(ENV, "Input port %p has level %d", port, current); + LF_DEBUG(ENV, "Input port %p has level %d", port, current); return current; } diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index 423e6b2b..6dc534c4 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -233,7 +233,7 @@ void Scheduler_run(Scheduler *untyped_self) { tag_t next_tag; bool non_terminating = self->super.keep_alive || env->has_async_events; bool going_to_shutdown = false; - LF_INFO(SCHED, "Scheduler running with non_terminating=%d has_async_events=%d", non_terminating, + LF_DEBUG(SCHED, "Scheduler running with non_terminating=%d has_async_events=%d", non_terminating, env->has_async_events); env->enter_critical_section(env); @@ -243,7 +243,7 @@ void Scheduler_run(Scheduler *untyped_self) { LF_DEBUG(SCHED, "Next event is at %" PRId64 ":%" PRIu32, next_tag.time, next_tag.microstep); if (lf_tag_compare(next_tag, self->stop_tag) > 0) { - LF_INFO(SCHED, "Next event is beyond stop tag: %" PRId64 ":%" PRIu32, self->stop_tag.time, + LF_DEBUG(SCHED, "Next event is beyond stop tag: %" PRId64 ":%" PRIu32, self->stop_tag.time, self->stop_tag.microstep); next_tag = self->stop_tag; going_to_shutdown = true; diff --git a/test/lf/src/FederatedBank.lf b/test/lf/src/FederatedBank.lf index 0f179b47..b588e302 100644 --- a/test/lf/src/FederatedBank.lf +++ b/test/lf/src/FederatedBank.lf @@ -1,7 +1,7 @@ target uC { platform: Native, timeout: 1 sec, - logging: Warn + logging: Info } From f2b95fab102d4a549f2e1638333bc08df73162c3 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 17 Jan 2025 16:14:05 +0100 Subject: [PATCH 50/65] Do not timestamp logs for FlexPRET --- include/reactor-uc/logging.h | 11 +++++++++++ src/logging.c | 11 +++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/include/reactor-uc/logging.h b/include/reactor-uc/logging.h index ef41bf23..d859bf4f 100644 --- a/include/reactor-uc/logging.h +++ b/include/reactor-uc/logging.h @@ -12,7 +12,18 @@ #define LF_LOG_LEVEL_DEBUG 5 // Add color codes to the output +#ifndef LF_COLORIZE_LOGS #define LF_COLORIZE_LOGS 1 +#endif + +// Add timestamp to the logs +#if !defined(LF_TIMESTAMP_LOGS) && !defined(PLATFORM_FLEXPRET) +#define LF_TIMESTAMP_LOGS 1 +#else +#undef LF_TIMESTAMP_LOGS +#define LF_TIMESTAMP_LOGS 0 +#endif + // The default log level for any unspecified module #ifndef LF_LOG_LEVEL_ALL diff --git a/src/logging.c b/src/logging.c index 7ecc9ecb..d3cffefd 100644 --- a/src/logging.c +++ b/src/logging.c @@ -43,7 +43,7 @@ void log_message(int level, const char *module, const char *fmt, ...) { va_list args; va_start(args, fmt); -#ifdef LF_COLORIZE_LOGS +#if LF_COLORIZE_LOGS==1 switch (level) { case LF_LOG_LEVEL_ERROR: log_printf(ANSI_COLOR_RED); @@ -61,13 +61,20 @@ void log_message(int level, const char *module, const char *fmt, ...) { break; } #endif + + #if LF_TIMESTAMP_LOGS==1 instant_t timestamp = 0; if (_lf_environment) { timestamp = _lf_environment->get_elapsed_physical_time(_lf_environment); } log_printf("%" PRId64 " [%s] [%s] ", timestamp, level_str, module); + #else + + log_printf("[%s] [%s] ", level_str, module); + #endif + Platform_vprintf(fmt, args); -#ifdef LF_COLORIZE_LOGS +#if LF_COLORIZE_LOGS==1 log_printf(ANSI_COLOR_RESET); #endif log_printf("\n"); From d87c6a4598974d9f48c4f06d106273a78144e5bb Mon Sep 17 00:00:00 2001 From: erlingrj Date: Fri, 17 Jan 2025 16:50:17 +0100 Subject: [PATCH 51/65] Format --- include/reactor-uc/logging.h | 1 - src/logging.c | 12 ++++++------ src/schedulers/dynamic/scheduler.c | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/include/reactor-uc/logging.h b/include/reactor-uc/logging.h index d859bf4f..1a077433 100644 --- a/include/reactor-uc/logging.h +++ b/include/reactor-uc/logging.h @@ -24,7 +24,6 @@ #define LF_TIMESTAMP_LOGS 0 #endif - // The default log level for any unspecified module #ifndef LF_LOG_LEVEL_ALL #ifndef NDEBUG diff --git a/src/logging.c b/src/logging.c index d3cffefd..43eeba62 100644 --- a/src/logging.c +++ b/src/logging.c @@ -43,7 +43,7 @@ void log_message(int level, const char *module, const char *fmt, ...) { va_list args; va_start(args, fmt); -#if LF_COLORIZE_LOGS==1 +#if LF_COLORIZE_LOGS == 1 switch (level) { case LF_LOG_LEVEL_ERROR: log_printf(ANSI_COLOR_RED); @@ -62,19 +62,19 @@ void log_message(int level, const char *module, const char *fmt, ...) { } #endif - #if LF_TIMESTAMP_LOGS==1 +#if LF_TIMESTAMP_LOGS == 1 instant_t timestamp = 0; if (_lf_environment) { timestamp = _lf_environment->get_elapsed_physical_time(_lf_environment); } log_printf("%" PRId64 " [%s] [%s] ", timestamp, level_str, module); - #else +#else log_printf("[%s] [%s] ", level_str, module); - #endif - +#endif + Platform_vprintf(fmt, args); -#if LF_COLORIZE_LOGS==1 +#if LF_COLORIZE_LOGS == 1 log_printf(ANSI_COLOR_RESET); #endif log_printf("\n"); diff --git a/src/schedulers/dynamic/scheduler.c b/src/schedulers/dynamic/scheduler.c index 6dc534c4..7b84614f 100644 --- a/src/schedulers/dynamic/scheduler.c +++ b/src/schedulers/dynamic/scheduler.c @@ -234,7 +234,7 @@ void Scheduler_run(Scheduler *untyped_self) { bool non_terminating = self->super.keep_alive || env->has_async_events; bool going_to_shutdown = false; LF_DEBUG(SCHED, "Scheduler running with non_terminating=%d has_async_events=%d", non_terminating, - env->has_async_events); + env->has_async_events); env->enter_critical_section(env); @@ -244,7 +244,7 @@ void Scheduler_run(Scheduler *untyped_self) { if (lf_tag_compare(next_tag, self->stop_tag) > 0) { LF_DEBUG(SCHED, "Next event is beyond stop tag: %" PRId64 ":%" PRIu32, self->stop_tag.time, - self->stop_tag.microstep); + self->stop_tag.microstep); next_tag = self->stop_tag; going_to_shutdown = true; } From 1ff975f9ac7b73d0efc3db81980b4414351369bd Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Mon, 20 Jan 2025 17:23:16 +0100 Subject: [PATCH 52/65] Make RIOT compile --- examples/riot/coap_federated_lf/Makefile | 31 ++++++++++++++ .../coap_federated_lf/src/CoapFederatedLF.lf | 41 +++++++++++++++++++ .../org/lflang/generator/uc/UcIpAddress.kt | 21 ++++++---- .../lflang/generator/uc/UcNetworkChannel.kt | 22 ++++++---- make/riot/riot-lfc.mk | 10 ++++- test/lf/src/only_build/FederatedCoap.lf | 2 +- 6 files changed, 110 insertions(+), 17 deletions(-) create mode 100755 examples/riot/coap_federated_lf/Makefile create mode 100644 examples/riot/coap_federated_lf/src/CoapFederatedLF.lf diff --git a/examples/riot/coap_federated_lf/Makefile b/examples/riot/coap_federated_lf/Makefile new file mode 100755 index 00000000..74dedd6c --- /dev/null +++ b/examples/riot/coap_federated_lf/Makefile @@ -0,0 +1,31 @@ +REACTOR_UC_PATH ?= $(CURDIR)/../../../ + +# The name of the LF application inside "./src" to build/run/flash etc. +LF_MAIN ?= CoapFederatedLF +FEDERATION ?= r1 + +# Enable reactor-uc features +# CFLAGS += -DNETWORK_CHANNEL_TCP_POSIX +CFLAGS += -DNETWORK_CHANNEL_COAP_RIOT + +# Execute the LF compiler if build target is "all" +ifeq ($(MAKECMDGOALS),all) + _ := $(shell $(REACTOR_UC_PATH)/lfc/bin/lfc-dev src/$(LF_MAIN).lf) +endif + +# ---- RIOT specific configuration ---- +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../../../../RIOT + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(REACTOR_UC_PATH)/make/riot/riot-lfc.mk diff --git a/examples/riot/coap_federated_lf/src/CoapFederatedLF.lf b/examples/riot/coap_federated_lf/src/CoapFederatedLF.lf new file mode 100644 index 00000000..bb80be9e --- /dev/null +++ b/examples/riot/coap_federated_lf/src/CoapFederatedLF.lf @@ -0,0 +1,41 @@ +target uC { + platform: RIOT, + timeout: 1sec + } + + reactor Src(id: int = 0) { + output out: int + reaction(startup) -> out{= + printf("Hello from Src!\n"); + lf_set(out, self->id); + =} + } + + reactor Dst { + input in: int + state check: bool = false + reaction(startup) {= + printf("Hello from Dst!\n"); + =} + reaction(in) {= + printf("Received %d from Src\n", in->value); + validate(in->value == 42); + self->check = true; + env->request_shutdown(env); + =} + + reaction(shutdown) {= + validate(self->check); + =} + } + + federated reactor { + @interface_coap(name="if1", address="fe80::44e5:1bff:fee4:dac8") + r1 = new Src(id=42) + + @interface_coap(name="if1", address="fe80::8cc3:33ff:febb:1b3") + r2 = new Dst() + + @link(left="if1", right="if1") + r1.out -> r2.in + } \ No newline at end of file diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcIpAddress.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcIpAddress.kt index 0c4f9122..68f80419 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcIpAddress.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcIpAddress.kt @@ -4,6 +4,8 @@ import org.lflang.AttributeUtils.getInterfaceAttributes import org.lflang.lf.Attribute import java.math.BigInteger import java.util.concurrent.atomic.AtomicInteger +import java.net.InetAddress +import java.net.UnknownHostException /** A class representing an IPAddress, either v4 or v6. */ @@ -22,10 +24,12 @@ sealed class IPAddress { } companion object { - fun isValidIPv4(address: String): Boolean { - val regex = Regex("^([0-9]{1,3}\\.){3}[0-9]{1,3}$") - return regex.matches(address) && - address.split(".").all { it.toIntOrNull() in 0..255 } + fun isValidIPv4(ip: String): Boolean { + return try { + InetAddress.getByName(ip) is java.net.Inet4Address + } catch (e: UnknownHostException) { + false + } } } } @@ -36,9 +40,12 @@ sealed class IPAddress { } companion object { - fun isValidIPv6(address: String): Boolean { - val regex = Regex("(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4})|(::)") - return regex.matches(address) + fun isValidIPv6(ip: String): Boolean { + return try { + InetAddress.getByName(ip) is java.net.Inet6Address + } catch (e: UnknownHostException) { + false + } } } } diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt index 89e90af2..a9dfde5a 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -206,7 +206,7 @@ abstract class UcNetworkChannel( COAP_UDP_IP -> { val srcEp = (srcIf as UcCoapUdpIpInterface).createEndpoint() val destEp = (destIf as UcCoapUdpIpInterface).createEndpoint() - channel = UcCoapUdpIpChannel(srcEp, destEp, serverLhs) + channel = UcCoapUdpIpChannel(srcEp, destEp) } CUSTOM -> { @@ -242,16 +242,24 @@ class UcTcpIpChannel( class UcCoapUdpIpChannel( src: UcCoapUdpIpEndpoint, dest: UcCoapUdpIpEndpoint, - serverLhs: Boolean = true, -) : UcNetworkChannel(COAP_UDP_IP, src, dest, serverLhs) { - private val srcAddr = src.ipAddress.address - private val destAddr = dest.ipAddress.address + // TODO: In CoAP every node is a server and a client => default server to false for now +) : UcNetworkChannel(COAP_UDP_IP, src, dest, false) { + private val srcAddr = src + private val destAddr = dest + + private fun getIpProtocolFamily(ip: IPAddress): String { + return when (ip) { + is IPAddress.IPv4 -> "AF_INET" + is IPAddress.IPv6 -> "AF_INET6" + else -> throw IllegalArgumentException("Unknown IP address type") + } + } override fun generateChannelCtorSrc() = - "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcAddr else destAddr}\", AF_INET, ${serverLhs});" + "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${destAddr.ipAddress.address}\", ${getIpProtocolFamily(destAddr.ipAddress)});" override fun generateChannelCtorDest() = - "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcAddr else destAddr}\" AF_INET, ${!serverLhs});" + "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${srcAddr.ipAddress.address}\" ${getIpProtocolFamily(srcAddr.ipAddress)});" override val codeType: String diff --git a/make/riot/riot-lfc.mk b/make/riot/riot-lfc.mk index a2591fba..b334fe65 100644 --- a/make/riot/riot-lfc.mk +++ b/make/riot/riot-lfc.mk @@ -8,8 +8,14 @@ endif # Name of your RIOT application APPLICATION ?= $(LF_MAIN) -# Path of generated lf c-code -LF_SRC_GEN_PATH ?= $(CURDIR)/src-gen/$(LF_MAIN) +# Check if this is a federated program +ifdef FEDERATION + # Path of generated lf c-code + LF_SRC_GEN_PATH ?= $(CURDIR)/src-gen/$(LF_MAIN)/$(FEDERATION) +else + # Path of generated lf c-code + LF_SRC_GEN_PATH ?= $(CURDIR)/src-gen/$(LF_MAIN) +endif # Only include generated files if build target is not "clean" # In this case the src-gen folder was deleted diff --git a/test/lf/src/only_build/FederatedCoap.lf b/test/lf/src/only_build/FederatedCoap.lf index eec71082..94a098b6 100644 --- a/test/lf/src/only_build/FederatedCoap.lf +++ b/test/lf/src/only_build/FederatedCoap.lf @@ -29,6 +29,6 @@ federated reactor { @interface_coap(name="if1", address="127.0.0.1") r2 = new Dst() - @link(left="if1", right="if1", server_side="right") + @link(left="if1", right="if1") r1.out -> r2.in } From 90f63c5d9c54239451d0c90059c9f4b04089462f Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Mon, 20 Jan 2025 17:34:35 +0100 Subject: [PATCH 53/65] Fix makefile and missing comma --- examples/riot/coap_federated_lf/Makefile | 12 ++++++++---- .../org/lflang/generator/uc/UcNetworkChannel.kt | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/examples/riot/coap_federated_lf/Makefile b/examples/riot/coap_federated_lf/Makefile index 74dedd6c..9324052d 100755 --- a/examples/riot/coap_federated_lf/Makefile +++ b/examples/riot/coap_federated_lf/Makefile @@ -4,10 +4,6 @@ REACTOR_UC_PATH ?= $(CURDIR)/../../../ LF_MAIN ?= CoapFederatedLF FEDERATION ?= r1 -# Enable reactor-uc features -# CFLAGS += -DNETWORK_CHANNEL_TCP_POSIX -CFLAGS += -DNETWORK_CHANNEL_COAP_RIOT - # Execute the LF compiler if build target is "all" ifeq ($(MAKECMDGOALS),all) _ := $(shell $(REACTOR_UC_PATH)/lfc/bin/lfc-dev src/$(LF_MAIN).lf) @@ -28,4 +24,12 @@ DEVELHELP ?= 1 # Change this to 0 show compiler invocation lines by default: QUIET ?= 1 +# Enable reactor-uc features +CFLAGS += -DNETWORK_CHANNEL_COAP_RIOT + +# Configure CoAP retransmission timeout +CFLAGS += -DCONFIG_GCOAP_NO_RETRANS_BACKOFF=1 +CFLAGS += -DCONFIG_COAP_ACK_TIMEOUT_MS=400 +CFLAGS += -DCONFIG_COAP_MAX_RETRANSMIT=4 + include $(REACTOR_UC_PATH)/make/riot/riot-lfc.mk diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt index a9dfde5a..1ca6ce06 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -259,7 +259,7 @@ class UcCoapUdpIpChannel( "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${destAddr.ipAddress.address}\", ${getIpProtocolFamily(destAddr.ipAddress)});" override fun generateChannelCtorDest() = - "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${srcAddr.ipAddress.address}\" ${getIpProtocolFamily(srcAddr.ipAddress)});" + "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${srcAddr.ipAddress.address}\", ${getIpProtocolFamily(srcAddr.ipAddress)});" override val codeType: String From 1098b342998eb52bd65341226e040f9b09b7dd6d Mon Sep 17 00:00:00 2001 From: erlingrj Date: Tue, 21 Jan 2025 11:32:15 +0100 Subject: [PATCH 54/65] Remove build.sh in riot Lf example --- examples/riot/hello_lf/run/build.sh | 4 ---- 1 file changed, 4 deletions(-) delete mode 100755 examples/riot/hello_lf/run/build.sh diff --git a/examples/riot/hello_lf/run/build.sh b/examples/riot/hello_lf/run/build.sh deleted file mode 100755 index 80602f55..00000000 --- a/examples/riot/hello_lf/run/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/env bash - -${REACTOR_UC_PATH}/lfc/bin/lfc-dev src/HelloLF.lf -make all \ No newline at end of file From 7c59706c2fbd7ec6dfbde9631f41a00a8ed1b0a6 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Tue, 21 Jan 2025 11:43:46 +0100 Subject: [PATCH 55/65] Use global _lf_environment --- src/platform/posix/tcp_ip_channel.c | 17 +++-------------- src/platform/riot/coap_udp_ip_channel.c | 20 ++++++++------------ 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 38838a83..c3961361 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -31,9 +31,6 @@ #define TCP_IP_CHANNEL_DEBUG(fmt, ...) \ LF_DEBUG(NET, "TcpIpChannel: [%s] " fmt, self->is_server ? "server" : "client", ##__VA_ARGS__) -static bool _is_globals_initialized = false; -static Environment *_env; - // Forward declarations static void *_TcpIpChannel_worker_thread(void *untyped_self); @@ -56,7 +53,7 @@ static void _TcpIpChannel_update_state(TcpIpChannel *self, NetworkChannelState n // Inform runtime about new state if it changed from or to NETWORK_CHANNEL_STATE_CONNECTED if ((old_state == NETWORK_CHANNEL_STATE_CONNECTED && new_state != NETWORK_CHANNEL_STATE_CONNECTED) || (old_state != NETWORK_CHANNEL_STATE_CONNECTED && new_state == NETWORK_CHANNEL_STATE_CONNECTED)) { - _env->platform->new_async_event(_env->platform); + _lf_environment->platform->new_async_event(_lf_environment->platform); } TCP_IP_CHANNEL_DEBUG("Update state: %s => %s done", NetworkChannel_state_to_string(self->state), NetworkChannel_state_to_string(new_state)); @@ -420,12 +417,12 @@ static void *_TcpIpChannel_worker_thread(void *untyped_self) { } break; case NETWORK_CHANNEL_STATE_CONNECTION_IN_PROGRESS: { - _env->platform->wait_for(_env->platform, self->super.expected_connect_duration); + _lf_environment->platform->wait_for(_lf_environment->platform, self->super.expected_connect_duration); } break; case NETWORK_CHANNEL_STATE_LOST_CONNECTION: case NETWORK_CHANNEL_STATE_CONNECTION_FAILED: { - _env->platform->wait_for(_env->platform, self->super.expected_connect_duration); + _lf_environment->platform->wait_for(_lf_environment->platform, self->super.expected_connect_duration); _TcpIpChannel_reset_socket(self); _TcpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_OPEN); } break; @@ -539,14 +536,6 @@ void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, u assert(env != NULL); assert(host != NULL); - // Initialize global coap server if not already done - if (!_is_globals_initialized) { - _is_globals_initialized = true; - - // Set environment - _env = env; - } - if (pthread_mutex_init(&self->mutex, NULL) != 0) { throw("Failed to initialize mutex"); } diff --git a/src/platform/riot/coap_udp_ip_channel.c b/src/platform/riot/coap_udp_ip_channel.c index 4b0afde6..65719ef3 100644 --- a/src/platform/riot/coap_udp_ip_channel.c +++ b/src/platform/riot/coap_udp_ip_channel.c @@ -15,8 +15,7 @@ char _connection_thread_stack[THREAD_STACKSIZE_MAIN]; int _connection_thread_pid = 0; -static bool _is_globals_initialized = false; -static Environment *_env; +static bool _coap_is_globals_initialized = false; static void _CoapUdpIpChannel_update_state(CoapUdpIpChannel *self, NetworkChannelState new_state) { COAP_UDP_IP_CHANNEL_DEBUG("Update state: %s => %s", NetworkChannel_state_to_string(self->state), @@ -37,7 +36,7 @@ static void _CoapUdpIpChannel_update_state(CoapUdpIpChannel *self, NetworkChanne // Inform runtime about new state if it changed from or to NETWORK_CHANNEL_STATE_CONNECTED if ((old_state == NETWORK_CHANNEL_STATE_CONNECTED && new_state != NETWORK_CHANNEL_STATE_CONNECTED) || (old_state != NETWORK_CHANNEL_STATE_CONNECTED && new_state == NETWORK_CHANNEL_STATE_CONNECTED)) { - _env->platform->new_async_event(_env->platform); + _lf_environment->platform->new_async_event(_lf_environment->platform); } // Let connection thread evaluate new state of this channel @@ -60,7 +59,7 @@ static void _CoapUdpIpChannel_update_state_if_not(CoapUdpIpChannel *self, Networ mutex_unlock(&self->state_mutex); // Inform runtime about new state - _env->platform->new_async_event(_env->platform); + _lf_environment->platform->new_async_event(_lf_environment->platform); } static NetworkChannelState _CoapUdpIpChannel_get_state(CoapUdpIpChannel *self) { @@ -75,9 +74,9 @@ static NetworkChannelState _CoapUdpIpChannel_get_state(CoapUdpIpChannel *self) { static CoapUdpIpChannel *_CoapUdpIpChannel_get_coap_channel_by_remote(const sock_udp_ep_t *remote) { CoapUdpIpChannel *channel; - for (size_t i = 0; i < _env->net_bundles_size; i++) { - if (_env->net_bundles[i]->net_channel->type == NETWORK_CHANNEL_TYPE_COAP_UDP_IP) { - channel = (CoapUdpIpChannel *)_env->net_bundles[i]->net_channel; + for (size_t i = 0; i < _lf_environment->net_bundles_size; i++) { + if (_lf_environment->net_bundles[i]->net_channel->type == NETWORK_CHANNEL_TYPE_COAP_UDP_IP) { + channel = (CoapUdpIpChannel *)_lf_environment->net_bundles[i]->net_channel; if (sock_udp_ep_equal(&channel->remote, remote)) { return channel; @@ -411,11 +410,8 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, Environment *env, const char assert(remote_address != NULL); // Initialize global coap server if not already done - if (!_is_globals_initialized) { - _is_globals_initialized = true; - - // Set environment - _env = env; + if (!_coap_is_globals_initialized) { + _coap_is_globals_initialized = true; // Initialize coap server gcoap_register_listener(&_listener); From 2f2d634d23d1a74ebaf9f111ffb1bacedb6a6ae3 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Tue, 21 Jan 2025 11:51:11 +0100 Subject: [PATCH 56/65] Fix missing _lf_environment in test --- test/unit/tcp_channel_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/tcp_channel_test.c b/test/unit/tcp_channel_test.c index 7cf7f8f7..8beadb1c 100644 --- a/test/unit/tcp_channel_test.c +++ b/test/unit/tcp_channel_test.c @@ -16,7 +16,7 @@ Reactor parent; Environment env; -Environment *_lf_environment = NULL; +Environment *_lf_environment = &env; FederatedConnectionBundle server_bundle; FederatedConnectionBundle client_bundle; FederatedConnectionBundle *net_bundles[] = {&server_bundle, &client_bundle}; From 012e8b3c4a2196c62e645d038d4284eb184e8b24 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Tue, 21 Jan 2025 11:56:49 +0100 Subject: [PATCH 57/65] Dont need environment arg to TcpIp and CoapUdp ctors --- examples/posix/federated/receiver.c | 2 +- examples/posix/federated/sender.c | 2 +- examples/riot/coap_federated/receiver/main.c | 2 +- examples/riot/coap_federated/sender/main.c | 2 +- examples/zephyr/basic_federated/common/receiver.h | 2 +- .../zephyr/basic_federated/federated_sender/src/sender.c | 4 ++-- include/reactor-uc/platform/posix/tcp_ip_channel.h | 2 +- include/reactor-uc/platform/riot/coap_udp_ip_channel.h | 2 +- .../kotlin/org/lflang/generator/uc/UcNetworkChannel.kt | 8 ++++---- src/platform/posix/tcp_ip_channel.c | 3 +-- src/platform/riot/coap_udp_ip_channel.c | 2 +- test/platform/riot/coap_channel_test/main.c | 2 +- test/unit/tcp_channel_test.c | 4 ++-- 13 files changed, 18 insertions(+), 19 deletions(-) diff --git a/examples/posix/federated/receiver.c b/examples/posix/federated/receiver.c index a12f2fff..2acc4bf1 100644 --- a/examples/posix/federated/receiver.c +++ b/examples/posix/federated/receiver.c @@ -62,7 +62,7 @@ typedef struct { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Receiver, Sender) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - TcpIpChannel_ctor(&self->channel, parent->env, "127.0.0.1", PORT_NUM, AF_INET, false); + TcpIpChannel_ctor(&self->channel, "127.0.0.1", PORT_NUM, AF_INET, false); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(Receiver, in, deserialize_msg_t); } diff --git a/examples/posix/federated/sender.c b/examples/posix/federated/sender.c index 3b6e962e..36e49884 100644 --- a/examples/posix/federated/sender.c +++ b/examples/posix/federated/sender.c @@ -71,7 +71,7 @@ typedef struct { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - TcpIpChannel_ctor(&self->channel, parent->env, "127.0.0.1", PORT_NUM, AF_INET, true); + TcpIpChannel_ctor(&self->channel, "127.0.0.1", PORT_NUM, AF_INET, true); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); diff --git a/examples/riot/coap_federated/receiver/main.c b/examples/riot/coap_federated/receiver/main.c index 65978cdd..8afb351b 100755 --- a/examples/riot/coap_federated/receiver/main.c +++ b/examples/riot/coap_federated/receiver/main.c @@ -65,7 +65,7 @@ typedef struct { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Receiver, Sender) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - CoapUdpIpChannel_ctor(&self->channel, parent->env, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); + CoapUdpIpChannel_ctor(&self->channel, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(Receiver, in, deserialize_msg_t); } diff --git a/examples/riot/coap_federated/sender/main.c b/examples/riot/coap_federated/sender/main.c index b6d6ab79..0f0a8ac1 100755 --- a/examples/riot/coap_federated/sender/main.c +++ b/examples/riot/coap_federated/sender/main.c @@ -72,7 +72,7 @@ typedef struct { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - CoapUdpIpChannel_ctor(&self->channel, parent->env, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); + CoapUdpIpChannel_ctor(&self->channel, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(Sender, out, serialize_msg_t); } diff --git a/examples/zephyr/basic_federated/common/receiver.h b/examples/zephyr/basic_federated/common/receiver.h index 37fd1c59..aa1fa838 100644 --- a/examples/zephyr/basic_federated/common/receiver.h +++ b/examples/zephyr/basic_federated/common/receiver.h @@ -76,7 +76,7 @@ typedef struct { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Receiver, Sender) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - TcpIpChannel_ctor(&self->channel, parent->env, IP_ADDR, PORT_NUM, AF_INET, false); + TcpIpChannel_ctor(&self->channel, IP_ADDR, PORT_NUM, AF_INET, false); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(Receiver, in, deserialize_payload_default); } diff --git a/examples/zephyr/basic_federated/federated_sender/src/sender.c b/examples/zephyr/basic_federated/federated_sender/src/sender.c index 9f55d6b7..0fc87f48 100644 --- a/examples/zephyr/basic_federated/federated_sender/src/sender.c +++ b/examples/zephyr/basic_federated/federated_sender/src/sender.c @@ -116,7 +116,7 @@ typedef struct { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver1) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - TcpIpChannel_ctor(&self->channel, parent->env, "192.168.1.100", PORT_CONN_1, AF_INET, true); + TcpIpChannel_ctor(&self->channel, "192.168.1.100", PORT_CONN_1, AF_INET, true); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); @@ -125,7 +125,7 @@ LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver1) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver2) { LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); - TcpIpChannel_ctor(&self->channel, parent->env, "192.168.1.100", PORT_CONN_2, AF_INET, true); + TcpIpChannel_ctor(&self->channel, "192.168.1.100", PORT_CONN_2, AF_INET, true); LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); diff --git a/include/reactor-uc/platform/posix/tcp_ip_channel.h b/include/reactor-uc/platform/posix/tcp_ip_channel.h index 13eb4e0e..6900a9d2 100644 --- a/include/reactor-uc/platform/posix/tcp_ip_channel.h +++ b/include/reactor-uc/platform/posix/tcp_ip_channel.h @@ -53,7 +53,7 @@ struct TcpIpChannel { void (*receive_callback)(FederatedConnectionBundle *conn, const FederateMessage *message); }; -void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, unsigned short port, int protocol_family, +void TcpIpChannel_ctor(TcpIpChannel *self, const char *host, unsigned short port, int protocol_family, bool is_server); #endif diff --git a/include/reactor-uc/platform/riot/coap_udp_ip_channel.h b/include/reactor-uc/platform/riot/coap_udp_ip_channel.h index f1c33098..c7155898 100644 --- a/include/reactor-uc/platform/riot/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/riot/coap_udp_ip_channel.h @@ -31,7 +31,7 @@ struct CoapUdpIpChannel { void (*receive_callback)(FederatedConnectionBundle *conn, const FederateMessage *message); }; -void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, Environment *env, const char *remote_address, +void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, int remote_protocol_family); #endif diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt index 89e90af2..402fe959 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -230,10 +230,10 @@ class UcTcpIpChannel( private val destTcp = dest override fun generateChannelCtorSrc() = - "TcpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${serverLhs});" + "TcpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${serverLhs});" override fun generateChannelCtorDest() = - "TcpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${!serverLhs});" + "TcpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcTcp.ipAddress.address else destTcp.ipAddress.address}\", ${if (serverLhs) srcTcp.port else destTcp.port}, AF_INET, ${!serverLhs});" override val codeType: String get() = "TcpIpChannel" @@ -248,10 +248,10 @@ class UcCoapUdpIpChannel( private val destAddr = dest.ipAddress.address override fun generateChannelCtorSrc() = - "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcAddr else destAddr}\", AF_INET, ${serverLhs});" + "CoapUdpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcAddr else destAddr}\", AF_INET, ${serverLhs});" override fun generateChannelCtorDest() = - "CoapUdpIpChannel_ctor(&self->channel, parent->env, \"${if (serverLhs) srcAddr else destAddr}\" AF_INET, ${!serverLhs});" + "CoapUdpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcAddr else destAddr}\" AF_INET, ${!serverLhs});" override val codeType: String diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index c3961361..0807ce0e 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -530,10 +530,9 @@ static bool TcpIpChannel_was_ever_connected(NetworkChannel *untyped_self) { return self->was_ever_connected; } -void TcpIpChannel_ctor(TcpIpChannel *self, Environment *env, const char *host, unsigned short port, int protocol_family, +void TcpIpChannel_ctor(TcpIpChannel *self, const char *host, unsigned short port, int protocol_family, bool is_server) { assert(self != NULL); - assert(env != NULL); assert(host != NULL); if (pthread_mutex_init(&self->mutex, NULL) != 0) { diff --git a/src/platform/riot/coap_udp_ip_channel.c b/src/platform/riot/coap_udp_ip_channel.c index 65719ef3..8874517c 100644 --- a/src/platform/riot/coap_udp_ip_channel.c +++ b/src/platform/riot/coap_udp_ip_channel.c @@ -403,7 +403,7 @@ void *_CoapUdpIpChannel_connection_thread(void *arg) { return NULL; } -void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, Environment *env, const char *remote_address, +void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, int remote_protocol_family) { assert(self != NULL); assert(env != NULL); diff --git a/test/platform/riot/coap_channel_test/main.c b/test/platform/riot/coap_channel_test/main.c index 5b7a5a4a..b9ee35c6 100644 --- a/test/platform/riot/coap_channel_test/main.c +++ b/test/platform/riot/coap_channel_test/main.c @@ -31,7 +31,7 @@ void setUp(void) { env.net_bundles_size = 1; /* init channel */ - CoapUdpIpChannel_ctor(&_coap_channel, &env, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); + CoapUdpIpChannel_ctor(&_coap_channel, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); /* init bundle */ FederatedConnectionBundle_ctor(&bundle, &parent, channel, NULL, NULL, 0, NULL, NULL, 0); diff --git a/test/unit/tcp_channel_test.c b/test/unit/tcp_channel_test.c index 8beadb1c..8277ab40 100644 --- a/test/unit/tcp_channel_test.c +++ b/test/unit/tcp_channel_test.c @@ -36,10 +36,10 @@ void setUp(void) { env.net_bundles_size = 2; /* init server */ - TcpIpChannel_ctor(&_server_tcp_channel, &env, HOST, PORT, AF_INET, true); + TcpIpChannel_ctor(&_server_tcp_channel, HOST, PORT, AF_INET, true); /* init client */ - TcpIpChannel_ctor(&_client_tcp_channel, &env, HOST, PORT, AF_INET, false); + TcpIpChannel_ctor(&_client_tcp_channel, HOST, PORT, AF_INET, false); /* init bundles */ FederatedConnectionBundle_ctor(&server_bundle, &parent, server_channel, NULL, NULL, 0, NULL, NULL, 0); From d3717ce49625762cfade9994a46400c3e7fd3741 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Tue, 21 Jan 2025 16:10:49 +0100 Subject: [PATCH 58/65] Coap updates --- include/reactor-uc/platform/posix/tcp_ip_channel.h | 3 +-- include/reactor-uc/platform/riot/coap_udp_ip_channel.h | 3 +-- .../main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt | 4 ++-- src/platform/posix/tcp_ip_channel.c | 3 +-- src/platform/riot/coap_udp_ip_channel.c | 3 +-- test/lf/src/only_build/FederatedCoap.lf | 2 +- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/include/reactor-uc/platform/posix/tcp_ip_channel.h b/include/reactor-uc/platform/posix/tcp_ip_channel.h index 6900a9d2..20695fef 100644 --- a/include/reactor-uc/platform/posix/tcp_ip_channel.h +++ b/include/reactor-uc/platform/posix/tcp_ip_channel.h @@ -53,7 +53,6 @@ struct TcpIpChannel { void (*receive_callback)(FederatedConnectionBundle *conn, const FederateMessage *message); }; -void TcpIpChannel_ctor(TcpIpChannel *self, const char *host, unsigned short port, int protocol_family, - bool is_server); +void TcpIpChannel_ctor(TcpIpChannel *self, const char *host, unsigned short port, int protocol_family, bool is_server); #endif diff --git a/include/reactor-uc/platform/riot/coap_udp_ip_channel.h b/include/reactor-uc/platform/riot/coap_udp_ip_channel.h index c7155898..c2ca8016 100644 --- a/include/reactor-uc/platform/riot/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/riot/coap_udp_ip_channel.h @@ -31,7 +31,6 @@ struct CoapUdpIpChannel { void (*receive_callback)(FederatedConnectionBundle *conn, const FederateMessage *message); }; -void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, - int remote_protocol_family); +void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, int remote_protocol_family); #endif diff --git a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt index 402fe959..f7bcc613 100644 --- a/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt +++ b/lfc/core/src/main/kotlin/org/lflang/generator/uc/UcNetworkChannel.kt @@ -269,10 +269,10 @@ class UcCustomChannel( private val destArgs = if (destIface.args != null) ", ${destIface.args}" else "" override fun generateChannelCtorSrc() = - "${srcIface.name}_ctor(&self->channel, parent->env ${srcArgs});" + "${srcIface.name}_ctor(&self->channel, ${srcArgs});" override fun generateChannelCtorDest() = - "${destIface.name}_ctor(&self->channel, parent->env ${destArgs});" + "${destIface.name}_ctor(&self->channel, ${destArgs});" override val codeType: String get() = srcIface.name diff --git a/src/platform/posix/tcp_ip_channel.c b/src/platform/posix/tcp_ip_channel.c index 0807ce0e..5517d934 100644 --- a/src/platform/posix/tcp_ip_channel.c +++ b/src/platform/posix/tcp_ip_channel.c @@ -530,8 +530,7 @@ static bool TcpIpChannel_was_ever_connected(NetworkChannel *untyped_self) { return self->was_ever_connected; } -void TcpIpChannel_ctor(TcpIpChannel *self, const char *host, unsigned short port, int protocol_family, - bool is_server) { +void TcpIpChannel_ctor(TcpIpChannel *self, const char *host, unsigned short port, int protocol_family, bool is_server) { assert(self != NULL); assert(host != NULL); diff --git a/src/platform/riot/coap_udp_ip_channel.c b/src/platform/riot/coap_udp_ip_channel.c index 8874517c..9f4d9048 100644 --- a/src/platform/riot/coap_udp_ip_channel.c +++ b/src/platform/riot/coap_udp_ip_channel.c @@ -403,8 +403,7 @@ void *_CoapUdpIpChannel_connection_thread(void *arg) { return NULL; } -void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, - int remote_protocol_family) { +void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, int remote_protocol_family) { assert(self != NULL); assert(env != NULL); assert(remote_address != NULL); diff --git a/test/lf/src/only_build/FederatedCoap.lf b/test/lf/src/only_build/FederatedCoap.lf index eec71082..94a098b6 100644 --- a/test/lf/src/only_build/FederatedCoap.lf +++ b/test/lf/src/only_build/FederatedCoap.lf @@ -29,6 +29,6 @@ federated reactor { @interface_coap(name="if1", address="127.0.0.1") r2 = new Dst() - @link(left="if1", right="if1", server_side="right") + @link(left="if1", right="if1") r1.out -> r2.in } From bc2d9a49dc7044d03d1597f6836759fec265cd89 Mon Sep 17 00:00:00 2001 From: erlingrj Date: Tue, 21 Jan 2025 16:30:32 +0100 Subject: [PATCH 59/65] Remove stale ref --- src/platform/riot/coap_udp_ip_channel.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform/riot/coap_udp_ip_channel.c b/src/platform/riot/coap_udp_ip_channel.c index 9f4d9048..3c5d3b27 100644 --- a/src/platform/riot/coap_udp_ip_channel.c +++ b/src/platform/riot/coap_udp_ip_channel.c @@ -405,7 +405,6 @@ void *_CoapUdpIpChannel_connection_thread(void *arg) { void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, int remote_protocol_family) { assert(self != NULL); - assert(env != NULL); assert(remote_address != NULL); // Initialize global coap server if not already done From 6ea9cad7b86f24e1d46d3ee16a4ba7c6fa4103cc Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 21 Jan 2025 16:53:28 +0100 Subject: [PATCH 60/65] Fix APPLICATION name --- make/riot/riot-lfc.mk | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/make/riot/riot-lfc.mk b/make/riot/riot-lfc.mk index b334fe65..a0996f29 100644 --- a/make/riot/riot-lfc.mk +++ b/make/riot/riot-lfc.mk @@ -5,14 +5,17 @@ ifndef LF_MAIN $(error LF_MAIN is not defined. Please define it!) endif -# Name of your RIOT application -APPLICATION ?= $(LF_MAIN) - # Check if this is a federated program ifdef FEDERATION + # Name of your RIOT application + APPLICATION ?= $(LF_MAIN)-$(FEDERATION) + # Path of generated lf c-code LF_SRC_GEN_PATH ?= $(CURDIR)/src-gen/$(LF_MAIN)/$(FEDERATION) else + # Name of your RIOT application + APPLICATION ?= $(LF_MAIN) + # Path of generated lf c-code LF_SRC_GEN_PATH ?= $(CURDIR)/src-gen/$(LF_MAIN) endif From e31427929e4a5d3790b243c01133596f80cf1875 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 21 Jan 2025 17:29:11 +0100 Subject: [PATCH 61/65] Fix make clean --- examples/riot/coap_federated_lf/Makefile | 2 +- make/riot/riot-lfc.mk | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/examples/riot/coap_federated_lf/Makefile b/examples/riot/coap_federated_lf/Makefile index 9324052d..dd37029d 100755 --- a/examples/riot/coap_federated_lf/Makefile +++ b/examples/riot/coap_federated_lf/Makefile @@ -5,7 +5,7 @@ LF_MAIN ?= CoapFederatedLF FEDERATION ?= r1 # Execute the LF compiler if build target is "all" -ifeq ($(MAKECMDGOALS),all) +ifeq ($(firstword $(MAKECMDGOALS)),all) _ := $(shell $(REACTOR_UC_PATH)/lfc/bin/lfc-dev src/$(LF_MAIN).lf) endif diff --git a/make/riot/riot-lfc.mk b/make/riot/riot-lfc.mk index a0996f29..a5fbc5aa 100644 --- a/make/riot/riot-lfc.mk +++ b/make/riot/riot-lfc.mk @@ -5,6 +5,10 @@ ifndef LF_MAIN $(error LF_MAIN is not defined. Please define it!) endif +ifndef RIOTBASE + $(error RIOTBASE is not defined. Please define it!) +endif + # Check if this is a federated program ifdef FEDERATION # Name of your RIOT application @@ -22,9 +26,11 @@ endif # Only include generated files if build target is not "clean" # In this case the src-gen folder was deleted -ifeq ($(MAKECMDGOALS),clean) +ifeq ($(firstword $(MAKECMDGOALS)),clean) # Delete src-gen folder if build target is "clean" _ := $(shell rm -rf $(LF_SRC_GEN_PATH)) + + include $(RIOTBASE)/Makefile.include else # Include the Makefile of the generated target application include $(LF_SRC_GEN_PATH)/Makefile @@ -37,6 +43,7 @@ else # Include generated h files CFLAGS += -I$(LF_SRC_GEN_PATH) + + include $(RIOT_MK_DIR)/riot.mk endif -include $(RIOT_MK_DIR)/riot.mk From 823b923ff628ed07c69b8fdba1813d57f09b3f0c Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 21 Jan 2025 17:44:45 +0100 Subject: [PATCH 62/65] Add all riot examples to the CI again --- examples/riot/blinky/build.sh | 2 ++ examples/riot/buildAll.sh | 26 ++++++++---------------- examples/riot/coap_federated/build.sh | 3 +++ examples/riot/coap_federated_lf/build.sh | 3 +++ examples/riot/hello/build.sh | 2 ++ examples/riot/hello_lf/build.sh | 2 ++ 6 files changed, 20 insertions(+), 18 deletions(-) create mode 100755 examples/riot/blinky/build.sh create mode 100755 examples/riot/coap_federated/build.sh create mode 100755 examples/riot/coap_federated_lf/build.sh create mode 100755 examples/riot/hello/build.sh create mode 100755 examples/riot/hello_lf/build.sh diff --git a/examples/riot/blinky/build.sh b/examples/riot/blinky/build.sh new file mode 100755 index 00000000..fe9a489e --- /dev/null +++ b/examples/riot/blinky/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +make all \ No newline at end of file diff --git a/examples/riot/buildAll.sh b/examples/riot/buildAll.sh index cdc677fd..3f0cb33e 100755 --- a/examples/riot/buildAll.sh +++ b/examples/riot/buildAll.sh @@ -2,22 +2,12 @@ set -e -# List of folders -FOLDERS=("blinky" "hello" "hello_lf") - -# List of boards -BOARDS=("native" "nucleo-f429zi") - -# Iterate over each board -for board in "${BOARDS[@]}"; do - # Command to execute in each folder - COMMAND="make BOARD=$board all" - - # Iterate over each folder and execute the command - for dir in "${FOLDERS[@]}"; do - echo "Entering $dir" - pushd $dir - $COMMAND - popd - done +# Iterate over each folder and execute the command +for dir in ./*; do + if [ -d $dir ]; then + echo "Entering $dir" + pushd $dir + ./build.sh + popd + fi done diff --git a/examples/riot/coap_federated/build.sh b/examples/riot/coap_federated/build.sh new file mode 100755 index 00000000..c5255542 --- /dev/null +++ b/examples/riot/coap_federated/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +REMOTE_ADDRESS=fe80::8cc3:33ff:febb:1b3 make all -C ./sender +REMOTE_ADDRESS=fe80::8cc3:33ff:febb:1b3 make all -C ./receiver \ No newline at end of file diff --git a/examples/riot/coap_federated_lf/build.sh b/examples/riot/coap_federated_lf/build.sh new file mode 100755 index 00000000..3fc89d19 --- /dev/null +++ b/examples/riot/coap_federated_lf/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +FEDERATION=r1 make all +FEDERATION=r2 make all diff --git a/examples/riot/hello/build.sh b/examples/riot/hello/build.sh new file mode 100755 index 00000000..fe9a489e --- /dev/null +++ b/examples/riot/hello/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +make all \ No newline at end of file diff --git a/examples/riot/hello_lf/build.sh b/examples/riot/hello_lf/build.sh new file mode 100755 index 00000000..fe9a489e --- /dev/null +++ b/examples/riot/hello_lf/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +make all \ No newline at end of file From 56c29969e7ef061de3c7064b3874c0ace821ebbb Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 21 Jan 2025 17:47:43 +0100 Subject: [PATCH 63/65] Fix hello_lf example make clean command not working --- examples/riot/hello_lf/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/riot/hello_lf/Makefile b/examples/riot/hello_lf/Makefile index 6e8091e3..c8791d58 100755 --- a/examples/riot/hello_lf/Makefile +++ b/examples/riot/hello_lf/Makefile @@ -8,7 +8,7 @@ LF_MAIN ?= HelloLF # CFLAGS += -DNETWORK_CHANNEL_COAP_RIOT # Execute the LF compiler if build target is "all" -ifeq ($(MAKECMDGOALS),all) +ifeq ($(firstword $(MAKECMDGOALS)),all) _ := $(shell $(REACTOR_UC_PATH)/lfc/bin/lfc-dev src/$(LF_MAIN).lf) endif From 64b6bd46da8ece639a0dce947fcbb80097b366dc Mon Sep 17 00:00:00 2001 From: erlingrj Date: Wed, 22 Jan 2025 09:44:19 +0100 Subject: [PATCH 64/65] Rename FEDERATION -> FEDERATE --- examples/riot/coap_federated_lf/Makefile | 2 +- examples/riot/coap_federated_lf/build.sh | 4 ++-- make/riot/riot-lfc.mk | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/riot/coap_federated_lf/Makefile b/examples/riot/coap_federated_lf/Makefile index dd37029d..59789c7c 100755 --- a/examples/riot/coap_federated_lf/Makefile +++ b/examples/riot/coap_federated_lf/Makefile @@ -2,7 +2,7 @@ REACTOR_UC_PATH ?= $(CURDIR)/../../../ # The name of the LF application inside "./src" to build/run/flash etc. LF_MAIN ?= CoapFederatedLF -FEDERATION ?= r1 +FEDERATE ?= r1 # Execute the LF compiler if build target is "all" ifeq ($(firstword $(MAKECMDGOALS)),all) diff --git a/examples/riot/coap_federated_lf/build.sh b/examples/riot/coap_federated_lf/build.sh index 3fc89d19..f768df49 100755 --- a/examples/riot/coap_federated_lf/build.sh +++ b/examples/riot/coap_federated_lf/build.sh @@ -1,3 +1,3 @@ #!/bin/bash -FEDERATION=r1 make all -FEDERATION=r2 make all +FEDERATE=r1 make all +FEDERATE=r2 make all diff --git a/make/riot/riot-lfc.mk b/make/riot/riot-lfc.mk index a5fbc5aa..70f7d2e2 100644 --- a/make/riot/riot-lfc.mk +++ b/make/riot/riot-lfc.mk @@ -10,12 +10,12 @@ ifndef RIOTBASE endif # Check if this is a federated program -ifdef FEDERATION +ifdef FEDERATE # Name of your RIOT application - APPLICATION ?= $(LF_MAIN)-$(FEDERATION) + APPLICATION ?= $(LF_MAIN)-$(FEDERATE) # Path of generated lf c-code - LF_SRC_GEN_PATH ?= $(CURDIR)/src-gen/$(LF_MAIN)/$(FEDERATION) + LF_SRC_GEN_PATH ?= $(CURDIR)/src-gen/$(LF_MAIN)/$(FEDERATE) else # Name of your RIOT application APPLICATION ?= $(LF_MAIN) From 1d10fd3b6067dfc8eb6ebf4b36ffd41a68ea22d3 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Wed, 22 Jan 2025 13:47:09 +0100 Subject: [PATCH 65/65] Make build scripts more realistic --- examples/riot/coap_federated/build.sh | 2 +- examples/riot/coap_federated_lf/build.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/riot/coap_federated/build.sh b/examples/riot/coap_federated/build.sh index c5255542..c9f48eb6 100755 --- a/examples/riot/coap_federated/build.sh +++ b/examples/riot/coap_federated/build.sh @@ -1,3 +1,3 @@ #!/bin/bash REMOTE_ADDRESS=fe80::8cc3:33ff:febb:1b3 make all -C ./sender -REMOTE_ADDRESS=fe80::8cc3:33ff:febb:1b3 make all -C ./receiver \ No newline at end of file +REMOTE_ADDRESS=fe80::44e5:1bff:fee4:dac8 make all -C ./receiver \ No newline at end of file diff --git a/examples/riot/coap_federated_lf/build.sh b/examples/riot/coap_federated_lf/build.sh index f768df49..abce6dd9 100755 --- a/examples/riot/coap_federated_lf/build.sh +++ b/examples/riot/coap_federated_lf/build.sh @@ -1,3 +1,3 @@ #!/bin/bash -FEDERATE=r1 make all -FEDERATE=r2 make all +FEDERATE=r1 PORT=tap0 make all +FEDERATE=r2 PORT=tap1 make all