From 5144e03dcd91e61bb7c261fde60fdce7568cebec Mon Sep 17 00:00:00 2001 From: Lasse Rosenow <10547444+LasseRosenow@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:48:54 +0100 Subject: [PATCH] LFC federated RIOT fixes (#192) * Have LFC generate a main function that can optionally be included in the build * CI * CI * CI * Setup LFC in RIOT container * Switch to riot master * Fix * Install LFC deps in zephyr CI also * WIP * WIP * WIP: SimpleFederated.lf now compiles. But invoking CMake correctly to generate two binaries and a shell script in `bin` is not working * Generate a launch script for federated native * Make federated launch script executable * WIP * Rework the handling of connections * Various fixes to get all standalone tests to pass again * Formatting * More WIP * Formatting * More WIP * Add @interface attr * More WIP * More WIP * More WIP * All tests are passing * Refactor * Formatting * Refactorings * More docs * CI * Fixes * TcpIp fixes * Revert more tcp stuff * Avoid flooding log when a federate closes a socket * Remove merge mistake * Formatting * Fix posix federated * More minor fixes * Format * Minimum event queue of 2 * Remove some dead code * Also close send_failed socketpair on reset * Generate return 0 in main function * Only generate launch script when we target native * Set timeout of 1minute on our LF tests * Add some info prints * Add was_ever_connected API to network_channel * Fix some warnings in unit-tests * Avoid some unnecessary LF_INFO calls * Do not timestamp logs for FlexPRET * Format * Make RIOT compile * Fix makefile and missing comma * Remove build.sh in riot Lf example * Use global _lf_environment * Fix missing _lf_environment in test * Dont need environment arg to TcpIp and CoapUdp ctors * Coap updates * Remove stale ref * Fix APPLICATION name * Fix make clean * Add all riot examples to the CI again * Fix hello_lf example make clean command not working * Rename FEDERATION -> FEDERATE * Make build scripts more realistic --------- Co-authored-by: erlingrj --- examples/riot/blinky/build.sh | 2 + examples/riot/buildAll.sh | 26 ++++-------- examples/riot/coap_federated/build.sh | 3 ++ examples/riot/coap_federated_lf/Makefile | 35 ++++++++++++++++ examples/riot/coap_federated_lf/build.sh | 3 ++ .../coap_federated_lf/src/CoapFederatedLF.lf | 41 +++++++++++++++++++ examples/riot/hello/build.sh | 2 + examples/riot/hello_lf/Makefile | 2 +- examples/riot/hello_lf/build.sh | 2 + .../org/lflang/generator/uc/UcIpAddress.kt | 21 ++++++---- .../lflang/generator/uc/UcNetworkChannel.kt | 23 +++++++---- make/riot/riot-lfc.mk | 28 ++++++++++--- 12 files changed, 148 insertions(+), 40 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/Makefile create mode 100755 examples/riot/coap_federated_lf/build.sh create mode 100644 examples/riot/coap_federated_lf/src/CoapFederatedLF.lf 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..c9f48eb6 --- /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::44e5:1bff:fee4:dac8 make all -C ./receiver \ No newline at end of file diff --git a/examples/riot/coap_federated_lf/Makefile b/examples/riot/coap_federated_lf/Makefile new file mode 100755 index 00000000..59789c7c --- /dev/null +++ b/examples/riot/coap_federated_lf/Makefile @@ -0,0 +1,35 @@ +REACTOR_UC_PATH ?= $(CURDIR)/../../../ + +# The name of the LF application inside "./src" to build/run/flash etc. +LF_MAIN ?= CoapFederatedLF +FEDERATE ?= r1 + +# Execute the LF compiler if build target is "all" +ifeq ($(firstword $(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 + +# 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/examples/riot/coap_federated_lf/build.sh b/examples/riot/coap_federated_lf/build.sh new file mode 100755 index 00000000..abce6dd9 --- /dev/null +++ b/examples/riot/coap_federated_lf/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +FEDERATE=r1 PORT=tap0 make all +FEDERATE=r2 PORT=tap1 make all 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/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/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 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 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 f7bcc613..1ddc64d6 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,17 +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, \"${if (serverLhs) srcAddr else destAddr}\", AF_INET, ${serverLhs});" + "CoapUdpIpChannel_ctor(&self->channel, \"${destAddr.ipAddress.address}\", ${getIpProtocolFamily(destAddr.ipAddress)});" override fun generateChannelCtorDest() = - "CoapUdpIpChannel_ctor(&self->channel, \"${if (serverLhs) srcAddr else destAddr}\" AF_INET, ${!serverLhs});" - + "CoapUdpIpChannel_ctor(&self->channel, \"${srcAddr.ipAddress.address}\", ${getIpProtocolFamily(srcAddr.ipAddress)});" override val codeType: String get() = "CoapUdpIpChannel" diff --git a/make/riot/riot-lfc.mk b/make/riot/riot-lfc.mk index a2591fba..70f7d2e2 100644 --- a/make/riot/riot-lfc.mk +++ b/make/riot/riot-lfc.mk @@ -5,17 +5,32 @@ ifndef LF_MAIN $(error LF_MAIN is not defined. Please define it!) endif -# Name of your RIOT application -APPLICATION ?= $(LF_MAIN) +ifndef RIOTBASE + $(error RIOTBASE is not defined. Please define it!) +endif + +# Check if this is a federated program +ifdef FEDERATE + # Name of your RIOT application + APPLICATION ?= $(LF_MAIN)-$(FEDERATE) -# Path of generated lf c-code -LF_SRC_GEN_PATH ?= $(CURDIR)/src-gen/$(LF_MAIN) + # Path of generated lf c-code + LF_SRC_GEN_PATH ?= $(CURDIR)/src-gen/$(LF_MAIN)/$(FEDERATE) +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 # 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 @@ -28,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