From 3056e8f55fec89c9afcc52e7f09b377dff55d245 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Thu, 8 May 2025 11:43:18 +0200 Subject: [PATCH 01/24] Add libcoap submodule --- .gitmodules | 3 +++ external/libcoap | 1 + 2 files changed, 4 insertions(+) create mode 160000 external/libcoap diff --git a/.gitmodules b/.gitmodules index a6b49b86d..d47a9b9a3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "external/nanopb"] path = external/nanopb url = https://github.com/nanopb/nanopb.git +[submodule "external/libcoap"] + path = external/libcoap + url = git@github.com:obgm/libcoap.git diff --git a/external/libcoap b/external/libcoap new file mode 160000 index 000000000..daa4e05e1 --- /dev/null +++ b/external/libcoap @@ -0,0 +1 @@ +Subproject commit daa4e05e18f37f03e26f8452587179c16a4e92ca From 919c64bf0d2a1e90c17d2e18408a5705ff236558 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Mon, 12 May 2025 17:08:53 +0200 Subject: [PATCH 02/24] Remove libcoap and use it as system library --- .gitmodules | 5 +---- examples/posix/hello/hello.c | 2 +- external/libcoap | 1 - .../posix/coap_channel_test/CMakeLists.txt | 8 ++++++++ test/platform/posix/coap_channel_test/main.c | 12 ++++++++++++ test/platform/posix/coap_channel_test/run.sh | 16 ++++++++++++++++ test/platform/posix/runAll.sh | 13 +++++++++++++ 7 files changed, 51 insertions(+), 6 deletions(-) delete mode 160000 external/libcoap create mode 100644 test/platform/posix/coap_channel_test/CMakeLists.txt create mode 100644 test/platform/posix/coap_channel_test/main.c create mode 100755 test/platform/posix/coap_channel_test/run.sh create mode 100755 test/platform/posix/runAll.sh diff --git a/.gitmodules b/.gitmodules index d47a9b9a3..f0a194feb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,4 @@ url = https://github.com/ThrowTheSwitch/Unity.git [submodule "external/nanopb"] path = external/nanopb - url = https://github.com/nanopb/nanopb.git -[submodule "external/libcoap"] - path = external/libcoap - url = git@github.com:obgm/libcoap.git + url = https://github.com/nanopb/nanopb.git \ No newline at end of file diff --git a/examples/posix/hello/hello.c b/examples/posix/hello/hello.c index 5f6bb4f8b..c4c838474 100644 --- a/examples/posix/hello/hello.c +++ b/examples/posix/hello/hello.c @@ -1,5 +1,5 @@ #include "reactor-uc/reactor-uc.h" -#include "../../common/timer_source.h" +#include "../../common/timer_source.h" LF_DEFINE_REACTION_BODY(TimerSource, r) { LF_SCOPE_SELF(TimerSource); diff --git a/external/libcoap b/external/libcoap deleted file mode 160000 index daa4e05e1..000000000 --- a/external/libcoap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit daa4e05e18f37f03e26f8452587179c16a4e92ca diff --git a/test/platform/posix/coap_channel_test/CMakeLists.txt b/test/platform/posix/coap_channel_test/CMakeLists.txt new file mode 100644 index 000000000..f17b6f062 --- /dev/null +++ b/test/platform/posix/coap_channel_test/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.20.0) +project(reactor-uc-posix-coap-channel-test) + +set(PLATFORM "POSIX" CACHE STRING "Platform to target") +add_subdirectory(../../../../ reactor-uc) + +add_executable(app main.c) +target_link_libraries(app PRIVATE reactor-uc) \ No newline at end of file diff --git a/test/platform/posix/coap_channel_test/main.c b/test/platform/posix/coap_channel_test/main.c new file mode 100644 index 000000000..21fe52fb0 --- /dev/null +++ b/test/platform/posix/coap_channel_test/main.c @@ -0,0 +1,12 @@ +#include "reactor-uc/reactor-uc.h" +#include "../../../../examples/common/timer_source.h" + +LF_DEFINE_REACTION_BODY(TimerSource, r) { + LF_SCOPE_SELF(TimerSource); + LF_SCOPE_ENV(); + printf("TimerSource World @ " PRINTF_TIME "\n", env->get_elapsed_logical_time(env)); +} + +int main() { + lf_start(); +} \ No newline at end of file diff --git a/test/platform/posix/coap_channel_test/run.sh b/test/platform/posix/coap_channel_test/run.sh new file mode 100755 index 000000000..12d611510 --- /dev/null +++ b/test/platform/posix/coap_channel_test/run.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Make test +cmake -Bbuild +make -C build + +# Run test +./build/app + +# Evaluate test output +if [ $? -eq 0 ]; then + echo "All tests passed." +else + echo "$? tests failed." >&2 + exit 1 +fi \ No newline at end of file diff --git a/test/platform/posix/runAll.sh b/test/platform/posix/runAll.sh new file mode 100755 index 000000000..0c8050bca --- /dev/null +++ b/test/platform/posix/runAll.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +# Iterate over each folder and execute the command +for dir in ./*; do + if [ -d $dir ]; then + echo "Entering $dir" + pushd $dir + ./run.sh + popd + fi +done From be1849417b3d95c16bea6eb71587cd74949d0488 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Mon, 12 May 2025 17:39:45 +0200 Subject: [PATCH 03/24] Compiles --- .../posix/coap_channel_test/CMakeLists.txt | 9 ++ test/platform/posix/coap_channel_test/main.c | 146 +++++++++++++++++- test/platform/posix/coap_channel_test/run.sh | 8 + 3 files changed, 155 insertions(+), 8 deletions(-) diff --git a/test/platform/posix/coap_channel_test/CMakeLists.txt b/test/platform/posix/coap_channel_test/CMakeLists.txt index f17b6f062..b96d6c39e 100644 --- a/test/platform/posix/coap_channel_test/CMakeLists.txt +++ b/test/platform/posix/coap_channel_test/CMakeLists.txt @@ -5,4 +5,13 @@ set(PLATFORM "POSIX" CACHE STRING "Platform to target") add_subdirectory(../../../../ reactor-uc) add_executable(app main.c) + +# LIBCOAP START +find_library(LIBCOAP_LIBRARIES coap-3) +find_path(LIBCOAP_INCLUDE_DIRS coap3/coap.h) + +target_include_directories(app PRIVATE ${LIBCOAP_INCLUDE_DIRS}) +target_link_libraries(app PRIVATE ${LIBCOAP_LIBRARIES}) +# LIBCOAP END + target_link_libraries(app PRIVATE reactor-uc) \ No newline at end of file diff --git a/test/platform/posix/coap_channel_test/main.c b/test/platform/posix/coap_channel_test/main.c index 21fe52fb0..591869c63 100644 --- a/test/platform/posix/coap_channel_test/main.c +++ b/test/platform/posix/coap_channel_test/main.c @@ -1,12 +1,142 @@ -#include "reactor-uc/reactor-uc.h" -#include "../../../../examples/common/timer_source.h" +#include +#include "coap3/coap.h" -LF_DEFINE_REACTION_BODY(TimerSource, r) { - LF_SCOPE_SELF(TimerSource); - LF_SCOPE_ENV(); - printf("TimerSource World @ " PRINTF_TIME "\n", env->get_elapsed_logical_time(env)); +static int have_response = 0; + +#ifndef COAP_CLIENT_URI +#define COAP_CLIENT_URI "coap://coap.me/hello" +#endif + +int resolve_address(coap_str_const_t *host, uint16_t port, coap_address_t *dst, int scheme_hint_bits) { + int ret = 0; + coap_addr_info_t *addr_info; + + addr_info = + coap_resolve_address_info(host, port, port, port, port, AF_UNSPEC, scheme_hint_bits, COAP_RESOLVE_TYPE_REMOTE); + if (addr_info) { + ret = 1; + *dst = addr_info->addr; + } + + coap_free_address_info(addr_info); + return ret; } -int main() { - lf_start(); +int main(int argc, char *argv[]) { + coap_context_t *ctx = NULL; + coap_session_t *session = NULL; + coap_optlist_t *optlist = NULL; + coap_address_t dst; + coap_pdu_t *pdu = NULL; + int result = EXIT_FAILURE; + + int len; + int res; + unsigned int wait_ms; + coap_uri_t uri; + const char *coap_uri = COAP_CLIENT_URI; + int is_mcast; +#define BUFSIZE 100 + unsigned char scratch[BUFSIZE]; + + /* Support run-time defining of CoAP URIs */ + if (argc > 1) { + coap_uri = argv[1]; + } + + /* Initialize libcoap library */ + coap_startup(); + + /* Set logging level */ + coap_set_log_level(COAP_LOG_WARN); + + /* Parse the URI */ + len = coap_split_uri((const unsigned char *)coap_uri, strlen(coap_uri), &uri); + if (len != 0) { + coap_log_warn("Failed to parse uri %s\n", coap_uri); + goto finish; + } + + /* resolve destination address where server should be sent */ + len = resolve_address(&uri.host, uri.port, &dst, 1 << uri.scheme); + if (len <= 0) { + coap_log_warn("Failed to resolve address %*.*s\n", (int)uri.host.length, (int)uri.host.length, + (const char *)uri.host.s); + goto finish; + } + is_mcast = coap_is_mcast(&dst); + + /* create CoAP context and a client session */ + if (!(ctx = coap_new_context(NULL))) { + coap_log_emerg("cannot create libcoap context\n"); + goto finish; + } + /* Support large responses */ + coap_context_set_block_mode(ctx, COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY); + + if (uri.scheme == COAP_URI_SCHEME_COAP) { + session = coap_new_client_session(ctx, NULL, &dst, COAP_PROTO_UDP); + } else if (uri.scheme == COAP_URI_SCHEME_COAP_TCP) { + session = coap_new_client_session(ctx, NULL, &dst, COAP_PROTO_TCP); + } + if (!session) { + coap_log_emerg("cannot create client session\n"); + goto finish; + } + + /* construct CoAP message */ + pdu = coap_pdu_init(is_mcast ? COAP_MESSAGE_NON : COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET, + coap_new_message_id(session), coap_session_max_pdu_size(session)); + if (!pdu) { + coap_log_emerg("cannot create PDU\n"); + goto finish; + } + + /* Add option list (which will be sorted) to the PDU */ + len = coap_uri_into_options(&uri, &dst, &optlist, 1, scratch, sizeof(scratch)); + if (len) { + coap_log_warn("Failed to create options\n"); + goto finish; + } + + if (optlist) { + res = coap_add_optlist_pdu(pdu, &optlist); + if (res != 1) { + coap_log_warn("Failed to add options to PDU\n"); + goto finish; + } + } + + coap_show_pdu(COAP_LOG_WARN, pdu); + + /* and send the PDU */ + if (coap_send(session, pdu) == COAP_INVALID_MID) { + coap_log_err("cannot send CoAP pdu\n"); + goto finish; + } + + wait_ms = (coap_session_get_default_leisure(session).integer_part + 1) * 1000; + + while (have_response == 0 || is_mcast) { + res = coap_io_process(ctx, 1000); + if (res >= 0) { + if (wait_ms > 0) { + if ((unsigned)res >= wait_ms) { + printf("timeout\n"); + break; + } else { + wait_ms -= res; + } + } + } + } + + result = EXIT_SUCCESS; +finish: + coap_delete_optlist(optlist); + coap_session_release(session); + coap_free_context(ctx); + coap_cleanup(); + + return result; } \ No newline at end of file diff --git a/test/platform/posix/coap_channel_test/run.sh b/test/platform/posix/coap_channel_test/run.sh index 12d611510..c9d4e0413 100755 --- a/test/platform/posix/coap_channel_test/run.sh +++ b/test/platform/posix/coap_channel_test/run.sh @@ -4,6 +4,14 @@ cmake -Bbuild make -C build +# Evaluate make output +if [ $? -eq 0 ]; then + echo "All tests passed." +else + echo "$? tests failed." >&2 + exit 1 +fi + # Run test ./build/app From 70714c21d2bbf0fe56f9e12dbf8e6092050ce848 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Mon, 26 May 2025 17:19:29 +0200 Subject: [PATCH 04/24] Add response handler => it works! --- test/platform/posix/coap_channel_test/main.c | 26 ++++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/test/platform/posix/coap_channel_test/main.c b/test/platform/posix/coap_channel_test/main.c index 591869c63..86e721666 100644 --- a/test/platform/posix/coap_channel_test/main.c +++ b/test/platform/posix/coap_channel_test/main.c @@ -22,6 +22,22 @@ int resolve_address(coap_str_const_t *host, uint16_t port, coap_address_t *dst, return ret; } +static const coap_response_t message_handler(coap_session_t *session, const coap_pdu_t *sent, + const coap_pdu_t *received, const coap_mid_t mid) { + size_t len; + const uint8_t *databuf; + size_t offset; + size_t total; + + have_response = 1; + coap_show_pdu(COAP_LOG_WARN, received); + if (coap_get_data_large(received, &len, &databuf, &offset, &total)) { + fwrite(databuf, 1, len, stdout); + fwrite("\n", 1, 1, stdout); + } + return COAP_RESPONSE_OK; +} + int main(int argc, char *argv[]) { coap_context_t *ctx = NULL; coap_session_t *session = NULL; @@ -35,7 +51,6 @@ int main(int argc, char *argv[]) { unsigned int wait_ms; coap_uri_t uri; const char *coap_uri = COAP_CLIENT_URI; - int is_mcast; #define BUFSIZE 100 unsigned char scratch[BUFSIZE]; @@ -64,7 +79,6 @@ int main(int argc, char *argv[]) { (const char *)uri.host.s); goto finish; } - is_mcast = coap_is_mcast(&dst); /* create CoAP context and a client session */ if (!(ctx = coap_new_context(NULL))) { @@ -84,9 +98,11 @@ int main(int argc, char *argv[]) { goto finish; } + /* coap_register_response_handler(ctx, response_handler); */ + coap_register_response_handler(ctx, message_handler); /* construct CoAP message */ - pdu = coap_pdu_init(is_mcast ? COAP_MESSAGE_NON : COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET, - coap_new_message_id(session), coap_session_max_pdu_size(session)); + pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET, coap_new_message_id(session), + coap_session_max_pdu_size(session)); if (!pdu) { coap_log_emerg("cannot create PDU\n"); goto finish; @@ -117,7 +133,7 @@ int main(int argc, char *argv[]) { wait_ms = (coap_session_get_default_leisure(session).integer_part + 1) * 1000; - while (have_response == 0 || is_mcast) { + while (have_response == 0) { res = coap_io_process(ctx, 1000); if (res >= 0) { if (wait_ms > 0) { From 9a531879776768fdf6e76975ea815fbc0eed2661 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Mon, 26 May 2025 17:55:29 +0200 Subject: [PATCH 05/24] Add working server --- .../CMakeLists.txt | 0 .../main.c | 2 +- .../run.sh | 0 .../coap_channel_server_test/CMakeLists.txt | 17 ++++ .../posix/coap_channel_server_test/main.c | 91 +++++++++++++++++++ .../posix/coap_channel_server_test/run.sh | 24 +++++ 6 files changed, 133 insertions(+), 1 deletion(-) rename test/platform/posix/{coap_channel_test => coap_channel_client_test}/CMakeLists.txt (100%) rename test/platform/posix/{coap_channel_test => coap_channel_client_test}/main.c (98%) rename test/platform/posix/{coap_channel_test => coap_channel_client_test}/run.sh (100%) create mode 100644 test/platform/posix/coap_channel_server_test/CMakeLists.txt create mode 100644 test/platform/posix/coap_channel_server_test/main.c create mode 100755 test/platform/posix/coap_channel_server_test/run.sh diff --git a/test/platform/posix/coap_channel_test/CMakeLists.txt b/test/platform/posix/coap_channel_client_test/CMakeLists.txt similarity index 100% rename from test/platform/posix/coap_channel_test/CMakeLists.txt rename to test/platform/posix/coap_channel_client_test/CMakeLists.txt diff --git a/test/platform/posix/coap_channel_test/main.c b/test/platform/posix/coap_channel_client_test/main.c similarity index 98% rename from test/platform/posix/coap_channel_test/main.c rename to test/platform/posix/coap_channel_client_test/main.c index 86e721666..e0bb33059 100644 --- a/test/platform/posix/coap_channel_test/main.c +++ b/test/platform/posix/coap_channel_client_test/main.c @@ -4,7 +4,7 @@ static int have_response = 0; #ifndef COAP_CLIENT_URI -#define COAP_CLIENT_URI "coap://coap.me/hello" +#define COAP_CLIENT_URI "coap://127.0.0.1/hello" #endif int resolve_address(coap_str_const_t *host, uint16_t port, coap_address_t *dst, int scheme_hint_bits) { diff --git a/test/platform/posix/coap_channel_test/run.sh b/test/platform/posix/coap_channel_client_test/run.sh similarity index 100% rename from test/platform/posix/coap_channel_test/run.sh rename to test/platform/posix/coap_channel_client_test/run.sh diff --git a/test/platform/posix/coap_channel_server_test/CMakeLists.txt b/test/platform/posix/coap_channel_server_test/CMakeLists.txt new file mode 100644 index 000000000..b96d6c39e --- /dev/null +++ b/test/platform/posix/coap_channel_server_test/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.20.0) +project(reactor-uc-posix-coap-channel-test) + +set(PLATFORM "POSIX" CACHE STRING "Platform to target") +add_subdirectory(../../../../ reactor-uc) + +add_executable(app main.c) + +# LIBCOAP START +find_library(LIBCOAP_LIBRARIES coap-3) +find_path(LIBCOAP_INCLUDE_DIRS coap3/coap.h) + +target_include_directories(app PRIVATE ${LIBCOAP_INCLUDE_DIRS}) +target_link_libraries(app PRIVATE ${LIBCOAP_LIBRARIES}) +# LIBCOAP END + +target_link_libraries(app PRIVATE reactor-uc) \ No newline at end of file diff --git a/test/platform/posix/coap_channel_server_test/main.c b/test/platform/posix/coap_channel_server_test/main.c new file mode 100644 index 000000000..4936e932c --- /dev/null +++ b/test/platform/posix/coap_channel_server_test/main.c @@ -0,0 +1,91 @@ +#include +#include +#include "coap3/coap.h" + +#define COAP_LISTEN_UCAST_IP "127.0.0.1" + +void resource_handler_hello(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response) { + coap_show_pdu(COAP_LOG_WARN, request); + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); + coap_add_data(response, 5, (const uint8_t *)"world"); + coap_show_pdu(COAP_LOG_WARN, response); +} + +void resource_handler_hello_my(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response) { + coap_show_pdu(COAP_LOG_WARN, request); + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); + coap_add_data(response, 8, (const uint8_t *)"my world"); + coap_show_pdu(COAP_LOG_WARN, response); +} + +int main(void) { + coap_context_t *ctx = NULL; + coap_resource_t *resource = NULL; + int result = EXIT_FAILURE; + + uint32_t scheme_hint_bits; + coap_addr_info_t *info = NULL; + coap_addr_info_t *info_list = NULL; + coap_str_const_t *my_address = coap_make_str_const(COAP_LISTEN_UCAST_IP); + bool have_ep = false; + + /* Initialize libcoap library */ + coap_startup(); + + /* Set logging level */ + coap_set_log_level(COAP_LOG_WARN); + + /* Create CoAP context */ + ctx = coap_new_context(NULL); + if (!ctx) { + coap_log_emerg("cannot initialize context\n"); + goto finish; + } + + /* Let libcoap do the multi-block payload handling (if any) */ + coap_context_set_block_mode(ctx, COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY); + + scheme_hint_bits = coap_get_available_scheme_hint_bits(0, 0, COAP_PROTO_UDP); + info_list = coap_resolve_address_info(my_address, 0, 0, 0, 0, 0, scheme_hint_bits, COAP_RESOLVE_TYPE_LOCAL); + /* Create CoAP listening endpoint(s) */ + for (info = info_list; info != NULL; info = info->next) { + coap_endpoint_t *ep; + + ep = coap_new_endpoint(ctx, &info->addr, info->proto); + if (!ep) { + coap_log_warn("cannot create endpoint for CoAP proto %u\n", info->proto); + } else { + have_ep = true; + } + } + coap_free_address_info(info_list); + if (have_ep == false) { + coap_log_err("No context available for interface '%s'\n", (const char *)my_address->s); + goto finish; + } + + /* Create a resource that the server can respond to with information */ + resource = coap_resource_init(coap_make_str_const("hello"), 0); + coap_register_handler(resource, COAP_REQUEST_GET, resource_handler_hello); + coap_add_resource(ctx, resource); + + /* Create another resource that the server can respond to with information */ + resource = coap_resource_init(coap_make_str_const("hello/my"), 0); + coap_register_handler(resource, COAP_REQUEST_GET, resource_handler_hello_my); + coap_add_resource(ctx, resource); + + /* Handle any libcoap I/O requirements */ + while (true) { + coap_io_process(ctx, COAP_IO_WAIT); + } + + result = EXIT_SUCCESS; +finish: + + coap_free_context(ctx); + coap_cleanup(); + + return result; +} \ No newline at end of file diff --git a/test/platform/posix/coap_channel_server_test/run.sh b/test/platform/posix/coap_channel_server_test/run.sh new file mode 100755 index 000000000..c9d4e0413 --- /dev/null +++ b/test/platform/posix/coap_channel_server_test/run.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Make test +cmake -Bbuild +make -C build + +# Evaluate make output +if [ $? -eq 0 ]; then + echo "All tests passed." +else + echo "$? tests failed." >&2 + exit 1 +fi + +# Run test +./build/app + +# Evaluate test output +if [ $? -eq 0 ]; then + echo "All tests passed." +else + echo "$? tests failed." >&2 + exit 1 +fi \ No newline at end of file From f3c182ef8cf124620e3981b412e46d7baee7d313 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Sun, 15 Jun 2025 21:21:47 +0200 Subject: [PATCH 06/24] WIP tests are failing :( --- .gitmodules | 5 +- CMakeLists.txt | 39 ++ external/libcoap | 1 + include/reactor-uc/network_channel.h | 3 + .../platform/posix/coap_udp_ip_channel.h | 52 ++ src/network_channel.c | 3 + src/platform/posix/coap_udp_ip_channel.c | 597 ++++++++++++++++++ .../coap_channel_client_test/CMakeLists.txt | 8 - .../coap_channel_server_test/CMakeLists.txt | 8 - .../posix/coap_channel_server_test/main.c | 7 + .../posix/coap_channel_test/CMakeLists.txt | 22 + test/platform/posix/coap_channel_test/main.c | 108 ++++ test/platform/posix/coap_channel_test/run.sh | 24 + 13 files changed, 860 insertions(+), 17 deletions(-) create mode 160000 external/libcoap create mode 100644 include/reactor-uc/platform/posix/coap_udp_ip_channel.h create mode 100644 src/platform/posix/coap_udp_ip_channel.c create mode 100644 test/platform/posix/coap_channel_test/CMakeLists.txt create mode 100644 test/platform/posix/coap_channel_test/main.c create mode 100755 test/platform/posix/coap_channel_test/run.sh diff --git a/.gitmodules b/.gitmodules index f0a194feb..bc1adab93 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,7 @@ url = https://github.com/ThrowTheSwitch/Unity.git [submodule "external/nanopb"] path = external/nanopb - url = https://github.com/nanopb/nanopb.git \ No newline at end of file + url = https://github.com/nanopb/nanopb.git +[submodule "external/libcoap"] + path = external/libcoap + url = https://github.com/obgm/libcoap.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 66b128bc7..1bd8391c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,40 @@ set(NANOPB_PATH external/nanopb) if (PLATFORM STREQUAL "POSIX") add_library(reactor-uc STATIC ${SOURCES}) target_link_libraries(reactor-uc PRIVATE pthread) + + # Set up options before libcoap is included + set(COAP_DISABLE_DTLS ON CACHE BOOL "Disable DTLS" FORCE) + set(COAP_BUILD_DOCS OFF CACHE BOOL "Disable docs" FORCE) + set(COAP_BUILD_EXAMPLES OFF CACHE BOOL "Disable examples" FORCE) + set(COAP_BUILD_TESTS OFF CACHE BOOL "Disable tests" FORCE) + + # Add libcoap as a subdirectory + add_subdirectory(external/libcoap) + + # Include internal libcoap headers if needed + target_include_directories(reactor-uc PUBLIC + external/libcoap/include + external/libcoap/src + ) + + # Link with the libcoap static library + target_link_libraries(reactor-uc PRIVATE coap-3) + + + # TODO: Remove this! + # Add Unity as a subdirectory + add_subdirectory(external/Unity) + + # Include internal Unity headers if needed + target_include_directories(reactor-uc PUBLIC + external/Unity/src + ) + + add_library(Unity STATIC external/Unity/src/unity.c) + target_include_directories(Unity PUBLIC ${UNITY_DIR}/src) + + # Link with the Unity static library + target_link_libraries(reactor-uc PRIVATE Unity) elseif (PLATFORM STREQUAL "FLEXPRET") add_library(reactor-uc STATIC ${SOURCES}) add_subdirectory($ENV{FP_SDK_PATH} BINARY_DIR) @@ -87,6 +121,10 @@ if(NETWORK_CHANNEL_TCP_POSIX) target_compile_definitions(reactor-uc PRIVATE NETWORK_CHANNEL_TCP_POSIX) endif() +if(NETWORK_CHANNEL_COAP) + target_compile_definitions(reactor-uc PUBLIC NETWORK_CHANNEL_COAP) +endif() + if(FEDERATED) target_compile_definitions(reactor-uc PUBLIC FEDERATED) endif() @@ -109,3 +147,4 @@ if(BUILD_UNIT_TESTS) set_target_properties( Unity PROPERTIES C_CLANG_TIDY "") # Disable clang-tidy for this external lib. add_subdirectory(test/unit) endif() + diff --git a/external/libcoap b/external/libcoap new file mode 160000 index 000000000..17c3feeb0 --- /dev/null +++ b/external/libcoap @@ -0,0 +1 @@ +Subproject commit 17c3feeb0837b6583ed698c6d2430c8d19705dba diff --git a/include/reactor-uc/network_channel.h b/include/reactor-uc/network_channel.h index 8796528bf..1642990bb 100644 --- a/include/reactor-uc/network_channel.h +++ b/include/reactor-uc/network_channel.h @@ -132,6 +132,9 @@ struct AsyncNetworkChannel { #ifdef NETWORK_CHANNEL_TCP_POSIX #include "platform/posix/tcp_ip_channel.h" #endif +#ifdef NETWORK_CHANNEL_COAP +#include "platform/posix/coap_udp_ip_channel.h" +#endif #elif defined(PLATFORM_ZEPHYR) #ifdef NETWORK_CHANNEL_TCP_POSIX diff --git a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h new file mode 100644 index 000000000..27278901e --- /dev/null +++ b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h @@ -0,0 +1,52 @@ +#ifndef REACTOR_UC_COAP_UDP_IP_CHANNEL_H +#define REACTOR_UC_COAP_UDP_IP_CHANNEL_H +#include "reactor-uc/network_channel.h" +#include "reactor-uc/environment.h" +#include +#include + +#define COAP_UDP_IP_CHANNEL_EXPECTED_CONNECT_DURATION MSEC(10); +#define COAP_UDP_IP_CHANNEL_BUFFERSIZE 1024 +// #define COAP_UDP_IP_CHANNEL_NUM_RETRIES 255; +#define COAP_UDP_IP_CHANNEL_RECV_THREAD_STACK_SIZE 2048 +// #define COAP_UDP_IP_CHANNEL_RECV_THREAD_STACK_GUARD_SIZE 128 + +typedef struct CoapUdpIpChannel CoapUdpIpChannel; +typedef struct FederatedConnectionBundle FederatedConnectionBundle; + +typedef enum { + COAP_REQUEST_TYPE_NONE, + COAP_REQUEST_TYPE_CONNECT, + COAP_REQUEST_TYPE_MESSAGE, + COAP_REQUEST_TYPE_DISCONNECT +} coap_request_type_t; + +typedef struct CoapUdpIpChannel { + NetworkChannel super; + + // libcoap specific fields + coap_context_t *coap_context; + coap_session_t *session; + coap_address_t remote_addr; + + // Threading and synchronization + pthread_mutex_t state_mutex; + pthread_cond_t state_cond; + pthread_mutex_t send_mutex; + pthread_cond_t send_cond; + + NetworkChannelState state; + + bool send_ack_received; + FederateMessage output; + + coap_request_type_t last_request_type; + coap_mid_t last_request_mid; + + FederatedConnectionBundle *federated_connection; + void (*receive_callback)(FederatedConnectionBundle *conn, const FederateMessage *message); +} CoapUdpIpChannel; + +void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, int remote_protocol_family); + +#endif diff --git a/src/network_channel.c b/src/network_channel.c index f7744489b..2f8d7173b 100644 --- a/src/network_channel.c +++ b/src/network_channel.c @@ -4,6 +4,9 @@ #ifdef NETWORK_CHANNEL_TCP_POSIX #include "platform/posix/tcp_ip_channel.c" #endif +#ifdef NETWORK_CHANNEL_COAP +#include "platform/posix/coap_udp_ip_channel.c" +#endif #elif defined(PLATFORM_ZEPHYR) #ifdef NETWORK_CHANNEL_TCP_POSIX diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c new file mode 100644 index 000000000..986674391 --- /dev/null +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -0,0 +1,597 @@ +#include "reactor-uc/platform/posix/coap_udp_ip_channel.h" +#include "reactor-uc/logging.h" +#include "reactor-uc/environments/federated_environment.h" +#include "reactor-uc/serialization.h" + +#include +#include +#include +#include +#include +#include +#include + +#define COAP_UDP_IP_CHANNEL_ERR(fmt, ...) LF_ERR(NET, "CoapUdpIpChannel: " fmt, ##__VA_ARGS__) +#define COAP_UDP_IP_CHANNEL_WARN(fmt, ...) LF_WARN(NET, "CoapUdpIpChannel: " fmt, ##__VA_ARGS__) +#define COAP_UDP_IP_CHANNEL_INFO(fmt, ...) LF_INFO(NET, "CoapUdpIpChannel: " fmt, ##__VA_ARGS__) +#define COAP_UDP_IP_CHANNEL_DEBUG(fmt, ...) LF_DEBUG(NET, "CoapUdpIpChannel: " fmt, ##__VA_ARGS__) + +static coap_context_t *_coap_context = NULL; +static pthread_t _connection_thread = 0; +static bool _coap_is_globals_initialized = false; +static pthread_mutex_t _global_mutex = PTHREAD_MUTEX_INITIALIZER; + +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), + NetworkChannel_state_to_string(new_state)); + + // Store old state + NetworkChannelState old_state; + + // Update the state of the channel to its new state + pthread_mutex_lock(&self->state_mutex); + old_state = self->state; + self->state = new_state; + pthread_mutex_unlock(&self->state_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)) { + _lf_environment->platform->notify(_lf_environment->platform); + } + + // Signal connection thread to evaluate new state + pthread_cond_signal(&self->state_cond); +} + +static void _CoapUdpIpChannel_update_state_if_not(CoapUdpIpChannel *self, NetworkChannelState new_state, + NetworkChannelState if_not) { + // Update the state of the channel itself + pthread_mutex_lock(&self->state_mutex); + if (self->state != if_not) { + COAP_UDP_IP_CHANNEL_DEBUG("Update state: %s => %s", NetworkChannel_state_to_string(self->state), + NetworkChannel_state_to_string(new_state)); + self->state = new_state; + } + pthread_mutex_unlock(&self->state_mutex); + + // Inform runtime about new state + _lf_environment->platform->notify(_lf_environment->platform); +} + +static NetworkChannelState _CoapUdpIpChannel_get_state(CoapUdpIpChannel *self) { + NetworkChannelState state; + + pthread_mutex_lock(&self->state_mutex); + state = self->state; + pthread_mutex_unlock(&self->state_mutex); + + return state; +} + +static CoapUdpIpChannel *_CoapUdpIpChannel_get_coap_channel_by_session(coap_session_t *session) { + CoapUdpIpChannel *channel; + FederatedEnvironment *env = (FederatedEnvironment *)_lf_environment; + + const coap_address_t *remote_addr = coap_session_get_addr_remote(session); + + 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; + + if (coap_address_equals(&channel->remote_addr, remote_addr)) { + return channel; + } + } + } + + char addr_str[INET6_ADDRSTRLEN]; + coap_print_addr(remote_addr, (unsigned char *)addr_str, sizeof(addr_str)); + COAP_UDP_IP_CHANNEL_ERR("Channel not found by session (addr=%s)", addr_str); + return NULL; +} + +static coap_response_t _CoapUdpIpChannel_client_response_handler(coap_session_t *session, const coap_pdu_t *sent, + const coap_pdu_t *received, const coap_mid_t id) { + (void)sent; + CoapUdpIpChannel *self = _CoapUdpIpChannel_get_coap_channel_by_session(session); + if (self == NULL) { + return COAP_RESPONSE_FAIL; + } + + // Verify this is the NACK that is expected (messages are not in order guaranteed) + if (id != self->last_request_mid) { + COAP_UDP_IP_CHANNEL_WARN("Received response for unexpected MID: %d (expected: %d)", id, self->last_request_mid); + return COAP_RESPONSE_OK; // Ignore out-of-order responses + } + + coap_pdu_code_t code = coap_pdu_get_code(received); + bool success = (COAP_RESPONSE_CLASS(code) == 2); + + switch (self->last_request_type) { + case COAP_REQUEST_TYPE_CONNECT: + if (success) { + _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTED); + } else { + COAP_UDP_IP_CHANNEL_ERR("CONNECTION REJECTED => Try to connect again"); + _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTION_FAILED); + } + break; + + case COAP_REQUEST_TYPE_MESSAGE: + pthread_mutex_lock(&self->send_mutex); + self->send_ack_received = true; + if (!success) { + COAP_UDP_IP_CHANNEL_ERR("MESSAGE REJECTED"); + _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_LOST_CONNECTION); + } + pthread_cond_signal(&self->send_cond); + pthread_mutex_unlock(&self->send_mutex); + break; + + case COAP_REQUEST_TYPE_DISCONNECT: + break; + + default: + COAP_UDP_IP_CHANNEL_WARN("Received response for unknown request type: %d", self->last_request_type); + break; + } + + // Clear the last request info + self->last_request_type = COAP_REQUEST_TYPE_NONE; + self->last_request_mid = COAP_INVALID_MID; + + return COAP_RESPONSE_OK; +} + +static void _CoapUdpIpChannel_client_nack_handler(coap_session_t *session, const coap_pdu_t *sent, + const coap_nack_reason_t reason, const coap_mid_t mid) { + (void)sent; + (void)reason; + CoapUdpIpChannel *self = _CoapUdpIpChannel_get_coap_channel_by_session(session); + if (self == NULL) { + return; + } + + // Verify this is the NACK that is expected (messages are not in order guaranteed) + if (mid != self->last_request_mid) { + COAP_UDP_IP_CHANNEL_WARN("Received NACK for unexpected MID: %d (expected: %d)", mid, self->last_request_mid); + return; + } + + switch (self->last_request_type) { + case COAP_REQUEST_TYPE_CONNECT: + COAP_UDP_IP_CHANNEL_ERR("TIMEOUT => Try to connect again"); + _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTION_FAILED); + break; + + case COAP_REQUEST_TYPE_MESSAGE: + COAP_UDP_IP_CHANNEL_ERR("MESSAGE TIMEOUT"); + _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_LOST_CONNECTION); + pthread_mutex_lock(&self->send_mutex); + self->send_ack_received = true; + pthread_cond_signal(&self->send_cond); + pthread_mutex_unlock(&self->send_mutex); + break; + + case COAP_REQUEST_TYPE_DISCONNECT: + break; + + default: + COAP_UDP_IP_CHANNEL_WARN("Received NACK for unknown request type: %d", self->last_request_type); + break; + } + + // Clear the last request info + self->last_request_type = COAP_REQUEST_TYPE_NONE; + self->last_request_mid = COAP_INVALID_MID; +} + +static void _CoapUdpIpChannel_server_connect_handler(coap_resource_t *resource, coap_session_t *session, + const coap_pdu_t *request, const coap_string_t *query, + coap_pdu_t *response) { + (void)response; + (void)request; + (void)session; + (void)resource; + (void)query; + COAP_UDP_IP_CHANNEL_DEBUG("Server connect handler"); + CoapUdpIpChannel *self = _CoapUdpIpChannel_get_coap_channel_by_session(session); + + // Error => return 401 (unauthorized) + if (self == NULL) { + COAP_UDP_IP_CHANNEL_ERR("Server connect handler: Client has unknown IP address"); + coap_pdu_set_code(response, COAP_RESPONSE_CODE_UNAUTHORIZED); + return; + } + + // Error => return 503 (service unavailable) + if (_CoapUdpIpChannel_get_state(self) == NETWORK_CHANNEL_STATE_CLOSED) { + COAP_UDP_IP_CHANNEL_ERR("Server connect handler: Channel is closed"); + coap_pdu_set_code(response, COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE); + return; + } + + // Success => return 204 (no content) + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); + return; +} + +static void _CoapUdpIpChannel_server_disconnect_handler(coap_resource_t *resource, coap_session_t *session, + const coap_pdu_t *request, const coap_string_t *query, + coap_pdu_t *response) { + (void)resource; + (void)query; + (void)request; + COAP_UDP_IP_CHANNEL_DEBUG("Server disconnect handler"); + CoapUdpIpChannel *self = _CoapUdpIpChannel_get_coap_channel_by_session(session); + + // Error => return 401 (unauthorized) + if (self == NULL) { + COAP_UDP_IP_CHANNEL_ERR("Server disconnect handler: Client has unknown IP address"); + coap_pdu_set_code(response, COAP_RESPONSE_CODE_UNAUTHORIZED); + return; + } + + // Update state because it does not make sense to send data to a closed connection. + if (_CoapUdpIpChannel_get_state(self) != NETWORK_CHANNEL_STATE_UNINITIALIZED && + _CoapUdpIpChannel_get_state(self) != NETWORK_CHANNEL_STATE_CLOSED) { + _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CLOSED); + } + + // Success => return 204 (no content) + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); + return; +} + +static void _CoapUdpIpChannel_server_message_handler(coap_resource_t *resource, coap_session_t *session, + const coap_pdu_t *request, const coap_string_t *query, + coap_pdu_t *response) { + (void)resource; + (void)query; + COAP_UDP_IP_CHANNEL_DEBUG("Server message handler"); + CoapUdpIpChannel *self = _CoapUdpIpChannel_get_coap_channel_by_session(session); + + // Error => return 401 (unauthorized) + if (self == NULL) { + COAP_UDP_IP_CHANNEL_ERR("Server message handler: Client has unknown IP address"); + coap_pdu_set_code(response, COAP_RESPONSE_CODE_UNAUTHORIZED); + return; + } + + // Get payload from request + const uint8_t *payload; + size_t payload_len; + if (coap_get_data(request, &payload_len, &payload) == 0) { + COAP_UDP_IP_CHANNEL_ERR("Server message handler: No payload in request"); + coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); + return; + } + + // Deserialize received message + deserialize_from_protobuf(&self->output, payload, payload_len); + COAP_UDP_IP_CHANNEL_DEBUG("Server message handler: Server received message"); + + // Call registered receive callback to inform runtime about the new message + if (self->receive_callback) { + self->receive_callback(self->federated_connection, &self->output); + } + + // Respond to the other federate + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); + return; +} + +static bool _CoapUdpIpChannel_send_coap_message(CoapUdpIpChannel *self, const char *path, + const FederateMessage *message) { + if (!self->session) { + COAP_UDP_IP_CHANNEL_ERR("No session available"); + return false; + } + + coap_pdu_t *pdu = coap_new_pdu(COAP_MESSAGE_CON, COAP_REQUEST_CODE_POST, self->session); + if (!pdu) { + COAP_UDP_IP_CHANNEL_ERR("Failed to create PDU"); + return false; + } + + // Add URI path + coap_add_option(pdu, COAP_OPTION_URI_PATH, strlen(path), (const uint8_t *)path); + + // Add payload if message is provided + if (message) { + uint8_t payload_buffer[2048]; + int payload_len = serialize_to_protobuf(message, payload_buffer, sizeof(payload_buffer)); + + if (payload_len < 0) { + COAP_UDP_IP_CHANNEL_ERR("Could not encode protobuf"); + coap_delete_pdu(pdu); + return false; + } + + if ((unsigned long)payload_len > sizeof(payload_buffer)) { + COAP_UDP_IP_CHANNEL_ERR("Payload too large (%d > %zu)", payload_len, sizeof(payload_buffer)); + coap_delete_pdu(pdu); + return false; + } + + uint8_t content_format = COAP_MEDIATYPE_APPLICATION_OCTET_STREAM; + coap_add_option(pdu, COAP_OPTION_CONTENT_FORMAT, 1, &content_format); + coap_add_data(pdu, payload_len, payload_buffer); + } + + coap_mid_t mid = coap_send(self->session, pdu); + if (mid == COAP_INVALID_MID) { + COAP_UDP_IP_CHANNEL_ERR("Failed to send CoAP message"); + return false; + } + + // Track this request for response handling + if (strcmp(path, "connect") == 0) { + self->last_request_type = COAP_REQUEST_TYPE_CONNECT; + } else if (strcmp(path, "message") == 0) { + self->last_request_type = COAP_REQUEST_TYPE_MESSAGE; + } else if (strcmp(path, "disconnect") == 0) { + self->last_request_type = COAP_REQUEST_TYPE_DISCONNECT; + } else { + self->last_request_type = COAP_REQUEST_TYPE_NONE; + } + self->last_request_mid = mid; + + COAP_UDP_IP_CHANNEL_DEBUG("CoAP Message sent (MID: %d, Type: %d)", mid, self->last_request_type); + return true; +} + +static lf_ret_t _CoapUdpIpChannel_client_send_connect_message(CoapUdpIpChannel *self) { + if (!_CoapUdpIpChannel_send_coap_message(self, "connect", NULL)) { + _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CONNECTION_FAILED); + COAP_UDP_IP_CHANNEL_ERR("Open connection: Failed to send CoAP message"); + return LF_ERR; + } else { + _CoapUdpIpChannel_update_state_if_not(self, NETWORK_CHANNEL_STATE_CONNECTION_IN_PROGRESS, + NETWORK_CHANNEL_STATE_CONNECTED); + } + + return LF_OK; +} + +static lf_ret_t CoapUdpIpChannel_open_connection(NetworkChannel *untyped_self) { + COAP_UDP_IP_CHANNEL_DEBUG("Open connection"); + CoapUdpIpChannel *self = (CoapUdpIpChannel *)untyped_self; + + // Create client session + self->session = coap_new_client_session(self->coap_context, NULL, &self->remote_addr, COAP_PROTO_UDP); + if (!self->session) { + COAP_UDP_IP_CHANNEL_ERR("Failed to create client session"); + return LF_ERR; + } + + // Set response and NACK handlers + coap_register_response_handler(self->coap_context, _CoapUdpIpChannel_client_response_handler); + coap_register_nack_handler(self->coap_context, _CoapUdpIpChannel_client_nack_handler); + + _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_OPEN); + return LF_OK; +} + +static void CoapUdpIpChannel_close_connection(NetworkChannel *untyped_self) { + COAP_UDP_IP_CHANNEL_DEBUG("Close connection"); + CoapUdpIpChannel *self = (CoapUdpIpChannel *)untyped_self; + + // Immediately close the channel + _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_CLOSED); + + // Inform the other federate that the channel is closed + if (self->session) { + _CoapUdpIpChannel_send_coap_message(self, "disconnect", NULL); + coap_session_release(self->session); + self->session = NULL; + } +} + +static lf_ret_t CoapUdpIpChannel_send_blocking(NetworkChannel *untyped_self, const FederateMessage *message) { + COAP_UDP_IP_CHANNEL_DEBUG("Send blocking"); + CoapUdpIpChannel *self = (CoapUdpIpChannel *)untyped_self; + + // Send message + pthread_mutex_lock(&self->send_mutex); + self->send_ack_received = false; + pthread_mutex_unlock(&self->send_mutex); + + if (_CoapUdpIpChannel_send_coap_message(self, "message", message)) { + // Wait until the response handler confirms the ack or times out + pthread_mutex_lock(&self->send_mutex); + while (!self->send_ack_received) { + pthread_cond_wait(&self->send_cond, &self->send_mutex); + } + pthread_mutex_unlock(&self->send_mutex); + + if (_CoapUdpIpChannel_get_state(self) == NETWORK_CHANNEL_STATE_CONNECTED) { + return LF_OK; + } + } + + return LF_ERR; +} + +static void CoapUdpIpChannel_register_receive_callback(NetworkChannel *untyped_self, + void (*receive_callback)(FederatedConnectionBundle *conn, + const FederateMessage *msg), + FederatedConnectionBundle *conn) { + COAP_UDP_IP_CHANNEL_INFO("Register receive callback"); + CoapUdpIpChannel *self = (CoapUdpIpChannel *)untyped_self; + + self->receive_callback = receive_callback; + self->federated_connection = conn; +} + +static void CoapUdpIpChannel_free(NetworkChannel *untyped_self) { + COAP_UDP_IP_CHANNEL_DEBUG("Free"); + CoapUdpIpChannel *self = (CoapUdpIpChannel *)untyped_self; + + // Close session if active + if (self->session) { + coap_session_release(self->session); + self->session = NULL; + } + + // Clean up mutexes and condition variables + pthread_mutex_destroy(&self->state_mutex); + pthread_cond_destroy(&self->state_cond); + pthread_mutex_destroy(&self->send_mutex); + pthread_cond_destroy(&self->send_cond); +} + +static bool CoapUdpIpChannel_is_connected(NetworkChannel *untyped_self) { + CoapUdpIpChannel *self = (CoapUdpIpChannel *)untyped_self; + return _CoapUdpIpChannel_get_state(self) == NETWORK_CHANNEL_STATE_CONNECTED; +} + +void *_CoapUdpIpChannel_connection_thread(void *arg) { + (void)arg; + COAP_UDP_IP_CHANNEL_DEBUG("Start connection thread"); + + while (true) { + // Process CoAP events + if (_coap_context) { + coap_io_process(_coap_context, 1000); // 1 second timeout + } + + // Check all channels for state changes + FederatedEnvironment *env = (FederatedEnvironment *)_lf_environment; + 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) { + CoapUdpIpChannel *self = (CoapUdpIpChannel *)env->net_bundles[i]->net_channel; + + NetworkChannelState state = _CoapUdpIpChannel_get_state(self); + + switch (state) { + case NETWORK_CHANNEL_STATE_OPEN: + /* try to connect */ + _CoapUdpIpChannel_client_send_connect_message(self); + break; + + case NETWORK_CHANNEL_STATE_CONNECTION_IN_PROGRESS: + /* nothing to do */ + break; + + case NETWORK_CHANNEL_STATE_LOST_CONNECTION: + case NETWORK_CHANNEL_STATE_CONNECTION_FAILED: + /* try to reconnect */ + _CoapUdpIpChannel_client_send_connect_message(self); + break; + + case NETWORK_CHANNEL_STATE_CONNECTED: + case NETWORK_CHANNEL_STATE_UNINITIALIZED: + case NETWORK_CHANNEL_STATE_CLOSED: + break; + } + } + } + } + + return NULL; +} + +void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, int remote_protocol_family) { + assert(self != NULL); + assert(remote_address != NULL); + + // Initialize global coap context if not already done + pthread_mutex_lock(&_global_mutex); + if (!_coap_is_globals_initialized) { + _coap_is_globals_initialized = true; + + // Initialize libcoap + coap_startup(); + coap_set_log_level(COAP_LOG_DEBUG); + + // Create CoAP context with server endpoint + coap_address_t listen_addr; + coap_address_init(&listen_addr); + + if (remote_protocol_family == AF_INET) { + listen_addr.size = sizeof(struct sockaddr_in); + listen_addr.addr.sin.sin_family = AF_INET; + listen_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; + listen_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); + } else { + listen_addr.size = sizeof(struct sockaddr_in6); + listen_addr.addr.sin6.sin6_family = AF_INET6; + listen_addr.addr.sin6.sin6_addr = in6addr_any; + listen_addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT); + } + + _coap_context = coap_new_context(&listen_addr); + if (!_coap_context) { + COAP_UDP_IP_CHANNEL_ERR("Failed to create CoAP context"); + pthread_mutex_unlock(&_global_mutex); + return; + } + + // Create server resources + coap_resource_t *connect_resource = coap_resource_init(coap_make_str_const("connect"), 0); + coap_register_handler(connect_resource, COAP_REQUEST_POST, _CoapUdpIpChannel_server_connect_handler); + coap_add_resource(_coap_context, connect_resource); + + coap_resource_t *disconnect_resource = coap_resource_init(coap_make_str_const("disconnect"), 0); + coap_register_handler(disconnect_resource, COAP_REQUEST_POST, _CoapUdpIpChannel_server_disconnect_handler); + coap_add_resource(_coap_context, disconnect_resource); + + coap_resource_t *message_resource = coap_resource_init(coap_make_str_const("message"), 0); + coap_register_handler(message_resource, COAP_REQUEST_POST, _CoapUdpIpChannel_server_message_handler); + coap_add_resource(_coap_context, message_resource); + + // Create connection thread + if (pthread_create(&_connection_thread, NULL, _CoapUdpIpChannel_connection_thread, NULL) != 0) { + COAP_UDP_IP_CHANNEL_ERR("Failed to create connection thread"); + } + } + pthread_mutex_unlock(&_global_mutex); + + // Super fields + self->super.expected_connect_duration = COAP_UDP_IP_CHANNEL_EXPECTED_CONNECT_DURATION; + self->super.type = NETWORK_CHANNEL_TYPE_COAP_UDP_IP; + self->super.mode = NETWORK_CHANNEL_MODE_ASYNC; + self->super.is_connected = CoapUdpIpChannel_is_connected; + self->super.open_connection = CoapUdpIpChannel_open_connection; + self->super.close_connection = CoapUdpIpChannel_close_connection; + self->super.send_blocking = CoapUdpIpChannel_send_blocking; + self->super.register_receive_callback = CoapUdpIpChannel_register_receive_callback; + self->super.free = CoapUdpIpChannel_free; + + // Concrete fields + self->coap_context = _coap_context; + self->session = NULL; + self->receive_callback = NULL; + self->federated_connection = NULL; + self->state = NETWORK_CHANNEL_STATE_UNINITIALIZED; + self->send_ack_received = false; + + // Initialize mutexes and condition variables + pthread_mutex_init(&self->state_mutex, NULL); + pthread_cond_init(&self->state_cond, NULL); + pthread_mutex_init(&self->send_mutex, NULL); + pthread_cond_init(&self->send_cond, NULL); + + // Convert host to coap address + if (remote_protocol_family == AF_INET) { + coap_address_init(&self->remote_addr); + self->remote_addr.size = sizeof(struct sockaddr_in); + self->remote_addr.addr.sin.sin_family = AF_INET; + self->remote_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); + if (inet_pton(AF_INET, remote_address, &self->remote_addr.addr.sin.sin_addr) != 1) { + COAP_UDP_IP_CHANNEL_ERR("Error parsing IPv4 address"); + } + } else if (remote_protocol_family == AF_INET6) { + coap_address_init(&self->remote_addr); + self->remote_addr.size = sizeof(struct sockaddr_in6); + self->remote_addr.addr.sin6.sin6_family = AF_INET6; + self->remote_addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT); + if (inet_pton(AF_INET6, remote_address, &self->remote_addr.addr.sin6.sin6_addr) != 1) { + COAP_UDP_IP_CHANNEL_ERR("Error parsing IPv6 address"); + } + } else { + COAP_UDP_IP_CHANNEL_ERR("Unsupported protocol family"); + } +} \ No newline at end of file diff --git a/test/platform/posix/coap_channel_client_test/CMakeLists.txt b/test/platform/posix/coap_channel_client_test/CMakeLists.txt index b96d6c39e..b19f3794c 100644 --- a/test/platform/posix/coap_channel_client_test/CMakeLists.txt +++ b/test/platform/posix/coap_channel_client_test/CMakeLists.txt @@ -6,12 +6,4 @@ add_subdirectory(../../../../ reactor-uc) add_executable(app main.c) -# LIBCOAP START -find_library(LIBCOAP_LIBRARIES coap-3) -find_path(LIBCOAP_INCLUDE_DIRS coap3/coap.h) - -target_include_directories(app PRIVATE ${LIBCOAP_INCLUDE_DIRS}) -target_link_libraries(app PRIVATE ${LIBCOAP_LIBRARIES}) -# LIBCOAP END - target_link_libraries(app PRIVATE reactor-uc) \ No newline at end of file diff --git a/test/platform/posix/coap_channel_server_test/CMakeLists.txt b/test/platform/posix/coap_channel_server_test/CMakeLists.txt index b96d6c39e..b19f3794c 100644 --- a/test/platform/posix/coap_channel_server_test/CMakeLists.txt +++ b/test/platform/posix/coap_channel_server_test/CMakeLists.txt @@ -6,12 +6,4 @@ add_subdirectory(../../../../ reactor-uc) add_executable(app main.c) -# LIBCOAP START -find_library(LIBCOAP_LIBRARIES coap-3) -find_path(LIBCOAP_INCLUDE_DIRS coap3/coap.h) - -target_include_directories(app PRIVATE ${LIBCOAP_INCLUDE_DIRS}) -target_link_libraries(app PRIVATE ${LIBCOAP_LIBRARIES}) -# LIBCOAP END - target_link_libraries(app PRIVATE reactor-uc) \ No newline at end of file diff --git a/test/platform/posix/coap_channel_server_test/main.c b/test/platform/posix/coap_channel_server_test/main.c index 4936e932c..9f184dddc 100644 --- a/test/platform/posix/coap_channel_server_test/main.c +++ b/test/platform/posix/coap_channel_server_test/main.c @@ -6,6 +6,13 @@ void resource_handler_hello(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response) { + const coap_address_t *remote_addr = coap_session_get_addr_remote(session); + + char addr_str[INET6_ADDRSTRLEN + 8] = {0}; + if (remote_addr) { + coap_print_addr(remote_addr, (uint8_t *)addr_str, sizeof(addr_str)); + printf("Request from: %s\n", addr_str); + } coap_show_pdu(COAP_LOG_WARN, request); coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); coap_add_data(response, 5, (const uint8_t *)"world"); diff --git a/test/platform/posix/coap_channel_test/CMakeLists.txt b/test/platform/posix/coap_channel_test/CMakeLists.txt new file mode 100644 index 000000000..347d5eee2 --- /dev/null +++ b/test/platform/posix/coap_channel_test/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.20.0) +project(reactor-uc-posix-coap-channel-test) + +set(FEDERATED ON) +set(NETWORK_CHANNEL_COAP ON) +set(PLATFORM "POSIX" CACHE STRING "Platform to target") + +add_subdirectory(../../../../ reactor-uc) + +add_executable(app main.c) + +target_link_libraries(app PRIVATE reactor-uc) + + + + + + + + + + diff --git a/test/platform/posix/coap_channel_test/main.c b/test/platform/posix/coap_channel_test/main.c new file mode 100644 index 000000000..659f8ddb8 --- /dev/null +++ b/test/platform/posix/coap_channel_test/main.c @@ -0,0 +1,108 @@ +#define FEDERATED 1 + +#include "reactor-uc/platform/posix/coap_udp_ip_channel.h" +#include "reactor-uc/reactor-uc.h" +#include "unity.h" +#include "../../../unit/test_util.h" +#include +#include +#include + +#define MESSAGE_CONTENT "Hello World1234" +#define MESSAGE_CONNECTION_ID 42 +#define REMOTE_ADDRESS "::1" +#define REMOTE_PROTOCOL_FAMILY AF_INET6 + +Reactor parent; +FederatedEnvironment env; +Environment *_lf_environment = &env.super; +FederatedConnectionBundle bundle; +FederatedConnectionBundle *net_bundles[] = {&bundle}; +StartupCoordinator startup_coordinator; + +CoapUdpIpChannel _coap_channel; +NetworkChannel *channel = &_coap_channel.super; + +bool server_callback_called = false; +bool client_callback_called = false; + +void setUp(void) { + /* init environment */ + FederatedEnvironment_ctor(&env, NULL, NULL, false, net_bundles, 1, &startup_coordinator, NULL); + + /* init channel */ + CoapUdpIpChannel_ctor(&_coap_channel, REMOTE_ADDRESS, REMOTE_PROTOCOL_FAMILY); + + /* init bundle */ + FederatedConnectionBundle_ctor(&bundle, &parent, channel, NULL, NULL, 0, NULL, NULL, 0, 0); +} + +void tearDown(void) { + channel->free(channel); +} + +/* TESTS */ +void test_open_connection_non_blocking(void) { + TEST_ASSERT_OK(channel->open_connection(channel)); + + printf("LOLOLOL\n"); + + sleep(1); + + TEST_ASSERT_TRUE(channel->is_connected(channel)); +} + +void server_callback_handler(FederatedConnectionBundle *self, const FederateMessage *_msg) { + (void)self; + const TaggedMessage *msg = &_msg->message.tagged_message; + printf("\nServer: Received message with connection number %" PRId32 " and content %s\n", msg->conn_id, + (char *)msg->payload.bytes); + TEST_ASSERT_EQUAL_STRING(MESSAGE_CONTENT, (char *)msg->payload.bytes); + TEST_ASSERT_EQUAL(MESSAGE_CONNECTION_ID, msg->conn_id); + + server_callback_called = true; +} + +void test_client_send_and_server_recv(void) { + // open connection + TEST_ASSERT_OK(channel->open_connection(channel)); + + // Wait until channel is connected + while (!channel->is_connected(channel)) { + sleep(1); + } + + // register receive callback for handling incoming messages + channel->register_receive_callback(channel, server_callback_handler, NULL); + + /* create message */ + FederateMessage msg; + msg.which_message = FederateMessage_tagged_message_tag; + + TaggedMessage *port_message = &msg.message.tagged_message; + port_message->conn_id = MESSAGE_CONNECTION_ID; + const char *message = MESSAGE_CONTENT; + memcpy(port_message->payload.bytes, message, sizeof(MESSAGE_CONTENT)); // NOLINT + port_message->payload.size = sizeof(MESSAGE_CONTENT); + + /* send message */ + TEST_ASSERT_OK(channel->send_blocking(channel, &msg)); + + /* wait for the callback */ + sleep(1); + + /* check if the callback was called */ + TEST_ASSERT_TRUE(server_callback_called); +} + +int main(void) { + printf("LOLOLOL\n"); + printf("LOLOLOL\n"); + printf("LOLOLOL\n"); + printf("LOLOLOL\n"); + printf("LOLOLOL\n"); + UNITY_BEGIN(); + RUN_TEST(test_open_connection_non_blocking); + RUN_TEST(test_client_send_and_server_recv); + exit(UNITY_END()); +} diff --git a/test/platform/posix/coap_channel_test/run.sh b/test/platform/posix/coap_channel_test/run.sh new file mode 100755 index 000000000..c9d4e0413 --- /dev/null +++ b/test/platform/posix/coap_channel_test/run.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Make test +cmake -Bbuild +make -C build + +# Evaluate make output +if [ $? -eq 0 ]; then + echo "All tests passed." +else + echo "$? tests failed." >&2 + exit 1 +fi + +# Run test +./build/app + +# Evaluate test output +if [ $? -eq 0 ]; then + echo "All tests passed." +else + echo "$? tests failed." >&2 + exit 1 +fi \ No newline at end of file From 595c28ec0a2a4108041677f6ba0ae760408afa2d Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 17 Jun 2025 16:57:58 +0200 Subject: [PATCH 07/24] Fix coap address compare port --- src/platform/posix/coap_udp_ip_channel.c | 11 ++++++++--- test/platform/posix/coap_channel_test/main.c | 7 ------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index 986674391..0f6207961 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -73,20 +73,25 @@ static CoapUdpIpChannel *_CoapUdpIpChannel_get_coap_channel_by_session(coap_sess CoapUdpIpChannel *channel; FederatedEnvironment *env = (FederatedEnvironment *)_lf_environment; - const coap_address_t *remote_addr = coap_session_get_addr_remote(session); + // Get the remote session address + coap_address_t remote_addr; + coap_address_copy(&remote_addr, coap_session_get_addr_remote(session)); + // Set incoming port to COAP_DEFAULT_PORT to ignore it in the address comparison + coap_address_set_port(&remote_addr, COAP_DEFAULT_PORT); 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; - if (coap_address_equals(&channel->remote_addr, remote_addr)) { + if (coap_address_equals(&channel->remote_addr, &remote_addr)) { return channel; } } } + // Debug print address char addr_str[INET6_ADDRSTRLEN]; - coap_print_addr(remote_addr, (unsigned char *)addr_str, sizeof(addr_str)); + coap_print_addr(&remote_addr, (unsigned char *)addr_str, sizeof(addr_str)); COAP_UDP_IP_CHANNEL_ERR("Channel not found by session (addr=%s)", addr_str); return NULL; } diff --git a/test/platform/posix/coap_channel_test/main.c b/test/platform/posix/coap_channel_test/main.c index 659f8ddb8..000114f5a 100644 --- a/test/platform/posix/coap_channel_test/main.c +++ b/test/platform/posix/coap_channel_test/main.c @@ -45,8 +45,6 @@ void tearDown(void) { void test_open_connection_non_blocking(void) { TEST_ASSERT_OK(channel->open_connection(channel)); - printf("LOLOLOL\n"); - sleep(1); TEST_ASSERT_TRUE(channel->is_connected(channel)); @@ -96,11 +94,6 @@ void test_client_send_and_server_recv(void) { } int main(void) { - printf("LOLOLOL\n"); - printf("LOLOLOL\n"); - printf("LOLOLOL\n"); - printf("LOLOLOL\n"); - printf("LOLOLOL\n"); UNITY_BEGIN(); RUN_TEST(test_open_connection_non_blocking); RUN_TEST(test_client_send_and_server_recv); From fdf705b3df320a221cd9a0f1a5b167b37ab85b21 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 17 Jun 2025 17:21:24 +0200 Subject: [PATCH 08/24] Tests work now, but it is a bit slow maybe? --- test/platform/posix/coap_channel_test/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/platform/posix/coap_channel_test/main.c b/test/platform/posix/coap_channel_test/main.c index 000114f5a..e5342fc9f 100644 --- a/test/platform/posix/coap_channel_test/main.c +++ b/test/platform/posix/coap_channel_test/main.c @@ -45,7 +45,7 @@ void tearDown(void) { void test_open_connection_non_blocking(void) { TEST_ASSERT_OK(channel->open_connection(channel)); - sleep(1); + sleep(2); // TODO: 2s is a bit long. 1s even is long. This should not need 2s but work with 1s or less even! TEST_ASSERT_TRUE(channel->is_connected(channel)); } From 8647c38fd36465ce9899819dc6b210f5ce216f08 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 17 Jun 2025 17:51:38 +0200 Subject: [PATCH 09/24] Cleanup useless boolean thanks to condition variables --- include/reactor-uc/platform/posix/coap_udp_ip_channel.h | 1 - src/platform/posix/coap_udp_ip_channel.c | 8 +------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h index 27278901e..aedb2e62a 100644 --- a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h @@ -37,7 +37,6 @@ typedef struct CoapUdpIpChannel { NetworkChannelState state; - bool send_ack_received; FederateMessage output; coap_request_type_t last_request_type; diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index 0f6207961..a2842945f 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -125,7 +125,6 @@ static coap_response_t _CoapUdpIpChannel_client_response_handler(coap_session_t case COAP_REQUEST_TYPE_MESSAGE: pthread_mutex_lock(&self->send_mutex); - self->send_ack_received = true; if (!success) { COAP_UDP_IP_CHANNEL_ERR("MESSAGE REJECTED"); _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_LOST_CONNECTION); @@ -174,7 +173,6 @@ static void _CoapUdpIpChannel_client_nack_handler(coap_session_t *session, const COAP_UDP_IP_CHANNEL_ERR("MESSAGE TIMEOUT"); _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_LOST_CONNECTION); pthread_mutex_lock(&self->send_mutex); - self->send_ack_received = true; pthread_cond_signal(&self->send_cond); pthread_mutex_unlock(&self->send_mutex); break; @@ -400,15 +398,12 @@ static lf_ret_t CoapUdpIpChannel_send_blocking(NetworkChannel *untyped_self, con // Send message pthread_mutex_lock(&self->send_mutex); - self->send_ack_received = false; pthread_mutex_unlock(&self->send_mutex); if (_CoapUdpIpChannel_send_coap_message(self, "message", message)) { // Wait until the response handler confirms the ack or times out pthread_mutex_lock(&self->send_mutex); - while (!self->send_ack_received) { - pthread_cond_wait(&self->send_cond, &self->send_mutex); - } + pthread_cond_wait(&self->send_cond, &self->send_mutex); pthread_mutex_unlock(&self->send_mutex); if (_CoapUdpIpChannel_get_state(self) == NETWORK_CHANNEL_STATE_CONNECTED) { @@ -571,7 +566,6 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, i self->receive_callback = NULL; self->federated_connection = NULL; self->state = NETWORK_CHANNEL_STATE_UNINITIALIZED; - self->send_ack_received = false; // Initialize mutexes and condition variables pthread_mutex_init(&self->state_mutex, NULL); From ce7154c24035453b4bfe6ed217812752b709bca7 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 24 Jun 2025 13:16:18 +0200 Subject: [PATCH 10/24] Migrate address parsing to use libcoap api --- src/platform/posix/coap_udp_ip_channel.c | 31 ++++++++---------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index a2842945f..1d3683fe5 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -5,9 +5,6 @@ #include #include -#include -#include -#include #include #include @@ -574,23 +571,15 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, i pthread_cond_init(&self->send_cond, NULL); // Convert host to coap address - if (remote_protocol_family == AF_INET) { - coap_address_init(&self->remote_addr); - self->remote_addr.size = sizeof(struct sockaddr_in); - self->remote_addr.addr.sin.sin_family = AF_INET; - self->remote_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); - if (inet_pton(AF_INET, remote_address, &self->remote_addr.addr.sin.sin_addr) != 1) { - COAP_UDP_IP_CHANNEL_ERR("Error parsing IPv4 address"); - } - } else if (remote_protocol_family == AF_INET6) { - coap_address_init(&self->remote_addr); - self->remote_addr.size = sizeof(struct sockaddr_in6); - self->remote_addr.addr.sin6.sin6_family = AF_INET6; - self->remote_addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT); - if (inet_pton(AF_INET6, remote_address, &self->remote_addr.addr.sin6.sin6_addr) != 1) { - COAP_UDP_IP_CHANNEL_ERR("Error parsing IPv6 address"); - } + coap_str_const_t *host_str = coap_make_str_const(remote_address); + int scheme_hint_bits = coap_get_available_scheme_hint_bits(0, 0, COAP_PROTO_UDP); + coap_addr_info_t *addr_info = + coap_resolve_address_info(host_str, COAP_DEFAULT_PORT, COAP_DEFAULT_PORT, 0, 0, remote_protocol_family, + scheme_hint_bits, COAP_RESOLVE_TYPE_REMOTE); + if (addr_info) { + self->remote_addr = addr_info->addr; + coap_free_address_info(addr_info); } else { - COAP_UDP_IP_CHANNEL_ERR("Unsupported protocol family"); + COAP_UDP_IP_CHANNEL_ERR("Error resolving remote address: %s", remote_address); } -} \ No newline at end of file +} From 4af3ade209e1504e74b499ae04c32057dafc1baa Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 24 Jun 2025 16:08:24 +0200 Subject: [PATCH 11/24] remote_address => remote_host and document coap channel constructors --- .../reactor-uc/platform/posix/coap_udp_ip_channel.h | 11 ++++++++++- .../reactor-uc/platform/riot/coap_udp_ip_channel.h | 11 ++++++++++- src/platform/posix/coap_udp_ip_channel.c | 8 ++++---- src/platform/riot/coap_udp_ip_channel.c | 7 ++++--- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h index aedb2e62a..14801dd13 100644 --- a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h @@ -46,6 +46,15 @@ typedef struct CoapUdpIpChannel { void (*receive_callback)(FederatedConnectionBundle *conn, const FederateMessage *message); } CoapUdpIpChannel; -void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, int remote_protocol_family); +/** + * @brief Constructor for the CoapUdpIpChannel. + * + * Initializes a CoapUdpIpChannel instance with the specified remote host and protocol family. + * + * @param self Pointer to the CoapUdpIpChannel instance. + * @param remote_host The remote host address, hostname or domain E.g. 127.0.0.1, [::1] or hostname.local. + * @param remote_protocol_family The protocol family (e.g., AF_INET for IPv4 and AF_INET6 for IPv6). + */ +void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_host, int remote_protocol_family); #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 9a0e2ad23..3a29652d8 100644 --- a/include/reactor-uc/platform/riot/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/riot/coap_udp_ip_channel.h @@ -30,6 +30,15 @@ struct CoapUdpIpChannel { void (*receive_callback)(FederatedConnectionBundle *conn, const FederateMessage *message); }; -void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, int remote_protocol_family); +/** + * @brief Constructor for the CoapUdpIpChannel. + * + * Initializes a CoapUdpIpChannel instance with the specified remote host and protocol family. + * + * @param self Pointer to the CoapUdpIpChannel instance. + * @param remote_host The remote host address, hostname or domain E.g. 127.0.0.1, [::1] or hostname.local. + * @param remote_protocol_family The protocol family (e.g., AF_INET for IPv4 and AF_INET6 for IPv6). + */ +void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_host, int remote_protocol_family); #endif diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index 1d3683fe5..28608e89b 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -490,9 +490,9 @@ 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_host, int remote_protocol_family) { assert(self != NULL); - assert(remote_address != NULL); + assert(remote_host != NULL); // Initialize global coap context if not already done pthread_mutex_lock(&_global_mutex); @@ -571,7 +571,7 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, i pthread_cond_init(&self->send_cond, NULL); // Convert host to coap address - coap_str_const_t *host_str = coap_make_str_const(remote_address); + coap_str_const_t *host_str = coap_make_str_const(remote_host); int scheme_hint_bits = coap_get_available_scheme_hint_bits(0, 0, COAP_PROTO_UDP); coap_addr_info_t *addr_info = coap_resolve_address_info(host_str, COAP_DEFAULT_PORT, COAP_DEFAULT_PORT, 0, 0, remote_protocol_family, @@ -580,6 +580,6 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, i self->remote_addr = addr_info->addr; coap_free_address_info(addr_info); } else { - COAP_UDP_IP_CHANNEL_ERR("Error resolving remote address: %s", remote_address); + COAP_UDP_IP_CHANNEL_ERR("Error resolving remote address: %s", remote_host); } } diff --git a/src/platform/riot/coap_udp_ip_channel.c b/src/platform/riot/coap_udp_ip_channel.c index db38ec224..1a56abf20 100644 --- a/src/platform/riot/coap_udp_ip_channel.c +++ b/src/platform/riot/coap_udp_ip_channel.c @@ -397,9 +397,9 @@ 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_host, int remote_protocol_family) { assert(self != NULL); - assert(remote_address != NULL); + assert(remote_host != NULL); // Initialize global coap server if not already done if (!_coap_is_globals_initialized) { @@ -432,7 +432,8 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_address, i self->state_mutex = (mutex_t)MUTEX_INIT; // Convert host to udp socket - if (inet_pton(remote_protocol_family, remote_address, self->remote.addr.ipv6) == 1) { + // TODO: Support hostnames and domain names + if (inet_pton(remote_protocol_family, remote_host, self->remote.addr.ipv6) == 1) { self->remote.family = remote_protocol_family; self->remote.port = CONFIG_GCOAP_PORT; } else { From 50e27f1f6b9f5c6a23ab5652715192ec86f2edbc Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 24 Jun 2025 17:00:04 +0200 Subject: [PATCH 12/24] Cleanup server initialization code --- src/platform/posix/coap_udp_ip_channel.c | 47 +++++++++++++++--------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index 28608e89b..4acc53e3d 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -503,29 +503,42 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_host, int coap_startup(); coap_set_log_level(COAP_LOG_DEBUG); - // Create CoAP context with server endpoint - coap_address_t listen_addr; - coap_address_init(&listen_addr); - - if (remote_protocol_family == AF_INET) { - listen_addr.size = sizeof(struct sockaddr_in); - listen_addr.addr.sin.sin_family = AF_INET; - listen_addr.addr.sin.sin_addr.s_addr = INADDR_ANY; - listen_addr.addr.sin.sin_port = htons(COAP_DEFAULT_PORT); - } else { - listen_addr.size = sizeof(struct sockaddr_in6); - listen_addr.addr.sin6.sin6_family = AF_INET6; - listen_addr.addr.sin6.sin6_addr = in6addr_any; - listen_addr.addr.sin6.sin6_port = htons(COAP_DEFAULT_PORT); - } - - _coap_context = coap_new_context(&listen_addr); + // Create CoAP context and endpoints + _coap_context = coap_new_context(NULL); if (!_coap_context) { COAP_UDP_IP_CHANNEL_ERR("Failed to create CoAP context"); pthread_mutex_unlock(&_global_mutex); return; } + // Let libcoap handle multi-block payloads + coap_context_set_block_mode(_coap_context, COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY); + + // Create CoAP listening endpoint(s) + int scheme_hint_bits = coap_get_available_scheme_hint_bits(0, 0, COAP_PROTO_UDP); + coap_addr_info_t *info_list = coap_resolve_address_info(NULL, COAP_DEFAULT_PORT, 0, 0, 0, AF_UNSPEC, + scheme_hint_bits, COAP_RESOLVE_TYPE_LOCAL); + + bool endpoint_created = false; + for (coap_addr_info_t *info = info_list; info != NULL; info = info->next) { + coap_endpoint_t *ep = coap_new_endpoint(_coap_context, &info->addr, info->proto); + if (ep) { + endpoint_created = true; + COAP_UDP_IP_CHANNEL_DEBUG("Created CoAP endpoint for protocol %u", info->proto); + } else { + COAP_UDP_IP_CHANNEL_WARN("Failed to create endpoint for protocol %u", info->proto); + } + } + coap_free_address_info(info_list); + + if (!endpoint_created) { + COAP_UDP_IP_CHANNEL_ERR("Failed to create any CoAP endpoints"); + coap_free_context(_coap_context); + _coap_context = NULL; + pthread_mutex_unlock(&_global_mutex); + return; + } + // Create server resources coap_resource_t *connect_resource = coap_resource_init(coap_make_str_const("connect"), 0); coap_register_handler(connect_resource, COAP_REQUEST_POST, _CoapUdpIpChannel_server_connect_handler); From 9c9fefea1624940182dc5507730081787ef65e08 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 24 Jun 2025 17:40:40 +0200 Subject: [PATCH 13/24] Correctly set receive thread stack size --- src/platform/posix/coap_udp_ip_channel.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index 4acc53e3d..383d5145c 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -553,9 +553,13 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_host, int coap_add_resource(_coap_context, message_resource); // Create connection thread - if (pthread_create(&_connection_thread, NULL, _CoapUdpIpChannel_connection_thread, NULL) != 0) { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, COAP_UDP_IP_CHANNEL_BUFFERSIZE); + if (pthread_create(&_connection_thread, &attr, _CoapUdpIpChannel_connection_thread, NULL) != 0) { COAP_UDP_IP_CHANNEL_ERR("Failed to create connection thread"); } + pthread_attr_destroy(&attr); } pthread_mutex_unlock(&_global_mutex); From 3d269610d2490196205e91a85abe5e9fa9a2f47b Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 24 Jun 2025 17:46:03 +0200 Subject: [PATCH 14/24] Fix stack size and use buf size define as well --- include/reactor-uc/platform/posix/coap_udp_ip_channel.h | 2 -- src/platform/posix/coap_udp_ip_channel.c | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h index 14801dd13..36880f920 100644 --- a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h @@ -7,9 +7,7 @@ #define COAP_UDP_IP_CHANNEL_EXPECTED_CONNECT_DURATION MSEC(10); #define COAP_UDP_IP_CHANNEL_BUFFERSIZE 1024 -// #define COAP_UDP_IP_CHANNEL_NUM_RETRIES 255; #define COAP_UDP_IP_CHANNEL_RECV_THREAD_STACK_SIZE 2048 -// #define COAP_UDP_IP_CHANNEL_RECV_THREAD_STACK_GUARD_SIZE 128 typedef struct CoapUdpIpChannel CoapUdpIpChannel; typedef struct FederatedConnectionBundle FederatedConnectionBundle; diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index 383d5145c..07fb722a3 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -300,7 +300,7 @@ static bool _CoapUdpIpChannel_send_coap_message(CoapUdpIpChannel *self, const ch // Add payload if message is provided if (message) { - uint8_t payload_buffer[2048]; + uint8_t payload_buffer[COAP_UDP_IP_CHANNEL_BUFFERSIZE]; int payload_len = serialize_to_protobuf(message, payload_buffer, sizeof(payload_buffer)); if (payload_len < 0) { @@ -555,7 +555,7 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_host, int // Create connection thread pthread_attr_t attr; pthread_attr_init(&attr); - pthread_attr_setstacksize(&attr, COAP_UDP_IP_CHANNEL_BUFFERSIZE); + pthread_attr_setstacksize(&attr, COAP_UDP_IP_CHANNEL_RECV_THREAD_STACK_SIZE); if (pthread_create(&_connection_thread, &attr, _CoapUdpIpChannel_connection_thread, NULL) != 0) { COAP_UDP_IP_CHANNEL_ERR("Failed to create connection thread"); } From ee7e10bc379d39323777756a709991f264287681 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 24 Jun 2025 18:13:55 +0200 Subject: [PATCH 15/24] Add comments --- include/reactor-uc/platform/posix/coap_udp_ip_channel.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h index 36880f920..5fcceb93c 100644 --- a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h @@ -37,6 +37,7 @@ typedef struct CoapUdpIpChannel { FederateMessage output; + // Handle message callbacks coap_request_type_t last_request_type; coap_mid_t last_request_mid; From 7db6c86f1de278f2259b9836e6f843a44e9161d0 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 24 Jun 2025 19:13:02 +0200 Subject: [PATCH 16/24] Remove redundant remote_address field --- .../platform/posix/coap_udp_ip_channel.h | 1 - src/platform/posix/coap_udp_ip_channel.c | 17 ++++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h index 5fcceb93c..8aaf42768 100644 --- a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h @@ -25,7 +25,6 @@ typedef struct CoapUdpIpChannel { // libcoap specific fields coap_context_t *coap_context; coap_session_t *session; - coap_address_t remote_addr; // Threading and synchronization pthread_mutex_t state_mutex; diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index 07fb722a3..1e659401a 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -80,7 +80,7 @@ static CoapUdpIpChannel *_CoapUdpIpChannel_get_coap_channel_by_session(coap_sess if (env->net_bundles[i]->net_channel->type == NETWORK_CHANNEL_TYPE_COAP_UDP_IP) { channel = (CoapUdpIpChannel *)env->net_bundles[i]->net_channel; - if (coap_address_equals(&channel->remote_addr, &remote_addr)) { + if (coap_address_equals(coap_session_get_addr_remote(channel->session), &remote_addr)) { return channel; } } @@ -359,13 +359,6 @@ static lf_ret_t CoapUdpIpChannel_open_connection(NetworkChannel *untyped_self) { COAP_UDP_IP_CHANNEL_DEBUG("Open connection"); CoapUdpIpChannel *self = (CoapUdpIpChannel *)untyped_self; - // Create client session - self->session = coap_new_client_session(self->coap_context, NULL, &self->remote_addr, COAP_PROTO_UDP); - if (!self->session) { - COAP_UDP_IP_CHANNEL_ERR("Failed to create client session"); - return LF_ERR; - } - // Set response and NACK handlers coap_register_response_handler(self->coap_context, _CoapUdpIpChannel_client_response_handler); coap_register_nack_handler(self->coap_context, _CoapUdpIpChannel_client_nack_handler); @@ -384,8 +377,6 @@ static void CoapUdpIpChannel_close_connection(NetworkChannel *untyped_self) { // Inform the other federate that the channel is closed if (self->session) { _CoapUdpIpChannel_send_coap_message(self, "disconnect", NULL); - coap_session_release(self->session); - self->session = NULL; } } @@ -594,7 +585,11 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_host, int coap_resolve_address_info(host_str, COAP_DEFAULT_PORT, COAP_DEFAULT_PORT, 0, 0, remote_protocol_family, scheme_hint_bits, COAP_RESOLVE_TYPE_REMOTE); if (addr_info) { - self->remote_addr = addr_info->addr; + // Create client session + self->session = coap_new_client_session(self->coap_context, NULL, &addr_info->addr, COAP_PROTO_UDP); + if (!self->session) { + COAP_UDP_IP_CHANNEL_ERR("Failed to create client session"); + } coap_free_address_info(addr_info); } else { COAP_UDP_IP_CHANNEL_ERR("Error resolving remote address: %s", remote_host); From 58f3000e2291e1f3ee947163b9be82bd9463088d Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 24 Jun 2025 19:15:12 +0200 Subject: [PATCH 17/24] Remove redundant coap_context field for each channel --- .../reactor-uc/platform/posix/coap_udp_ip_channel.h | 3 +-- src/platform/posix/coap_udp_ip_channel.c | 11 +++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h index 8aaf42768..2794c45b5 100644 --- a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h @@ -22,8 +22,7 @@ typedef enum { typedef struct CoapUdpIpChannel { NetworkChannel super; - // libcoap specific fields - coap_context_t *coap_context; + // Remote address etc. coap_session_t *session; // Threading and synchronization diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index 1e659401a..7a36aceeb 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -359,10 +359,6 @@ static lf_ret_t CoapUdpIpChannel_open_connection(NetworkChannel *untyped_self) { COAP_UDP_IP_CHANNEL_DEBUG("Open connection"); CoapUdpIpChannel *self = (CoapUdpIpChannel *)untyped_self; - // Set response and NACK handlers - coap_register_response_handler(self->coap_context, _CoapUdpIpChannel_client_response_handler); - coap_register_nack_handler(self->coap_context, _CoapUdpIpChannel_client_nack_handler); - _CoapUdpIpChannel_update_state(self, NETWORK_CHANNEL_STATE_OPEN); return LF_OK; } @@ -502,6 +498,10 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_host, int return; } + // Set response and NACK handlers + coap_register_response_handler(_coap_context, _CoapUdpIpChannel_client_response_handler); + coap_register_nack_handler(_coap_context, _CoapUdpIpChannel_client_nack_handler); + // Let libcoap handle multi-block payloads coap_context_set_block_mode(_coap_context, COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY); @@ -566,7 +566,6 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_host, int self->super.free = CoapUdpIpChannel_free; // Concrete fields - self->coap_context = _coap_context; self->session = NULL; self->receive_callback = NULL; self->federated_connection = NULL; @@ -586,7 +585,7 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_host, int scheme_hint_bits, COAP_RESOLVE_TYPE_REMOTE); if (addr_info) { // Create client session - self->session = coap_new_client_session(self->coap_context, NULL, &addr_info->addr, COAP_PROTO_UDP); + self->session = coap_new_client_session(_coap_context, NULL, &addr_info->addr, COAP_PROTO_UDP); if (!self->session) { COAP_UDP_IP_CHANNEL_ERR("Failed to create client session"); } From 14d844ce3781e31aed38bf18ceadbdb151b10816 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Tue, 24 Jun 2025 19:18:55 +0200 Subject: [PATCH 18/24] Cleanup more --- src/platform/posix/coap_udp_ip_channel.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index 7a36aceeb..0a50b54dc 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -5,8 +5,6 @@ #include #include -#include -#include #define COAP_UDP_IP_CHANNEL_ERR(fmt, ...) LF_ERR(NET, "CoapUdpIpChannel: " fmt, ##__VA_ARGS__) #define COAP_UDP_IP_CHANNEL_WARN(fmt, ...) LF_WARN(NET, "CoapUdpIpChannel: " fmt, ##__VA_ARGS__) From 5564c6d8d128c37c01dfde021160d8629adaee2e Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Wed, 25 Jun 2025 13:37:29 +0200 Subject: [PATCH 19/24] Fix send_blocking mutex --- src/platform/posix/coap_udp_ip_channel.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index 0a50b54dc..02d1048d0 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -380,12 +380,11 @@ static lf_ret_t CoapUdpIpChannel_send_blocking(NetworkChannel *untyped_self, con // Send message pthread_mutex_lock(&self->send_mutex); - pthread_mutex_unlock(&self->send_mutex); if (_CoapUdpIpChannel_send_coap_message(self, "message", message)) { // Wait until the response handler confirms the ack or times out - pthread_mutex_lock(&self->send_mutex); pthread_cond_wait(&self->send_cond, &self->send_mutex); + pthread_mutex_unlock(&self->send_mutex); if (_CoapUdpIpChannel_get_state(self) == NETWORK_CHANNEL_STATE_CONNECTED) { @@ -393,6 +392,8 @@ static lf_ret_t CoapUdpIpChannel_send_blocking(NetworkChannel *untyped_self, con } } + pthread_mutex_unlock(&self->send_mutex); + return LF_ERR; } From c7b82b884ce8d1a59f116baedb1c300c9b0c25cf Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Wed, 25 Jun 2025 13:40:18 +0200 Subject: [PATCH 20/24] Remove useless define semicolon --- include/reactor-uc/platform/posix/coap_udp_ip_channel.h | 2 +- include/reactor-uc/platform/riot/coap_udp_ip_channel.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h index 2794c45b5..171d91f1a 100644 --- a/include/reactor-uc/platform/posix/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/posix/coap_udp_ip_channel.h @@ -5,7 +5,7 @@ #include #include -#define COAP_UDP_IP_CHANNEL_EXPECTED_CONNECT_DURATION MSEC(10); +#define COAP_UDP_IP_CHANNEL_EXPECTED_CONNECT_DURATION MSEC(10) #define COAP_UDP_IP_CHANNEL_BUFFERSIZE 1024 #define COAP_UDP_IP_CHANNEL_RECV_THREAD_STACK_SIZE 2048 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 3a29652d8..8dd164cbd 100644 --- a/include/reactor-uc/platform/riot/coap_udp_ip_channel.h +++ b/include/reactor-uc/platform/riot/coap_udp_ip_channel.h @@ -5,9 +5,9 @@ #include "net/sock/udp.h" #include "mutex.h" -#define COAP_UDP_IP_CHANNEL_EXPECTED_CONNECT_DURATION MSEC(10); +#define COAP_UDP_IP_CHANNEL_EXPECTED_CONNECT_DURATION MSEC(10) #define COAP_UDP_IP_CHANNEL_BUFFERSIZE 1024 -// #define COAP_UDP_IP_CHANNEL_NUM_RETRIES 255; +// #define COAP_UDP_IP_CHANNEL_NUM_RETRIES 255 // #define COAP_UDP_IP_CHANNEL_RECV_THREAD_STACK_SIZE 2048 // #define COAP_UDP_IP_CHANNEL_RECV_THREAD_STACK_GUARD_SIZE 128 From be6fcff7745e02e3d51d7ca06d386fa8f8cdc101 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Wed, 25 Jun 2025 14:09:51 +0200 Subject: [PATCH 21/24] Document channel by session logic a little bit --- src/platform/posix/coap_udp_ip_channel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index 02d1048d0..d8c2ba76f 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -78,6 +78,7 @@ static CoapUdpIpChannel *_CoapUdpIpChannel_get_coap_channel_by_session(coap_sess if (env->net_bundles[i]->net_channel->type == NETWORK_CHANNEL_TYPE_COAP_UDP_IP) { channel = (CoapUdpIpChannel *)env->net_bundles[i]->net_channel; + // The port of the channel address is already using COAP_DEFAULT_PORT if (coap_address_equals(coap_session_get_addr_remote(channel->session), &remote_addr)) { return channel; } From dec99bf128056aedb4a987e1db13044020f247f6 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Wed, 25 Jun 2025 15:34:06 +0200 Subject: [PATCH 22/24] Remove old files --- .../coap_channel_client_test/CMakeLists.txt | 9 - .../posix/coap_channel_client_test/main.c | 158 ------------------ .../posix/coap_channel_client_test/run.sh | 24 --- .../coap_channel_server_test/CMakeLists.txt | 9 - .../posix/coap_channel_server_test/main.c | 98 ----------- .../posix/coap_channel_server_test/run.sh | 24 --- 6 files changed, 322 deletions(-) delete mode 100644 test/platform/posix/coap_channel_client_test/CMakeLists.txt delete mode 100644 test/platform/posix/coap_channel_client_test/main.c delete mode 100755 test/platform/posix/coap_channel_client_test/run.sh delete mode 100644 test/platform/posix/coap_channel_server_test/CMakeLists.txt delete mode 100644 test/platform/posix/coap_channel_server_test/main.c delete mode 100755 test/platform/posix/coap_channel_server_test/run.sh diff --git a/test/platform/posix/coap_channel_client_test/CMakeLists.txt b/test/platform/posix/coap_channel_client_test/CMakeLists.txt deleted file mode 100644 index b19f3794c..000000000 --- a/test/platform/posix/coap_channel_client_test/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -cmake_minimum_required(VERSION 3.20.0) -project(reactor-uc-posix-coap-channel-test) - -set(PLATFORM "POSIX" CACHE STRING "Platform to target") -add_subdirectory(../../../../ reactor-uc) - -add_executable(app main.c) - -target_link_libraries(app PRIVATE reactor-uc) \ No newline at end of file diff --git a/test/platform/posix/coap_channel_client_test/main.c b/test/platform/posix/coap_channel_client_test/main.c deleted file mode 100644 index e0bb33059..000000000 --- a/test/platform/posix/coap_channel_client_test/main.c +++ /dev/null @@ -1,158 +0,0 @@ -#include -#include "coap3/coap.h" - -static int have_response = 0; - -#ifndef COAP_CLIENT_URI -#define COAP_CLIENT_URI "coap://127.0.0.1/hello" -#endif - -int resolve_address(coap_str_const_t *host, uint16_t port, coap_address_t *dst, int scheme_hint_bits) { - int ret = 0; - coap_addr_info_t *addr_info; - - addr_info = - coap_resolve_address_info(host, port, port, port, port, AF_UNSPEC, scheme_hint_bits, COAP_RESOLVE_TYPE_REMOTE); - if (addr_info) { - ret = 1; - *dst = addr_info->addr; - } - - coap_free_address_info(addr_info); - return ret; -} - -static const coap_response_t message_handler(coap_session_t *session, const coap_pdu_t *sent, - const coap_pdu_t *received, const coap_mid_t mid) { - size_t len; - const uint8_t *databuf; - size_t offset; - size_t total; - - have_response = 1; - coap_show_pdu(COAP_LOG_WARN, received); - if (coap_get_data_large(received, &len, &databuf, &offset, &total)) { - fwrite(databuf, 1, len, stdout); - fwrite("\n", 1, 1, stdout); - } - return COAP_RESPONSE_OK; -} - -int main(int argc, char *argv[]) { - coap_context_t *ctx = NULL; - coap_session_t *session = NULL; - coap_optlist_t *optlist = NULL; - coap_address_t dst; - coap_pdu_t *pdu = NULL; - int result = EXIT_FAILURE; - - int len; - int res; - unsigned int wait_ms; - coap_uri_t uri; - const char *coap_uri = COAP_CLIENT_URI; -#define BUFSIZE 100 - unsigned char scratch[BUFSIZE]; - - /* Support run-time defining of CoAP URIs */ - if (argc > 1) { - coap_uri = argv[1]; - } - - /* Initialize libcoap library */ - coap_startup(); - - /* Set logging level */ - coap_set_log_level(COAP_LOG_WARN); - - /* Parse the URI */ - len = coap_split_uri((const unsigned char *)coap_uri, strlen(coap_uri), &uri); - if (len != 0) { - coap_log_warn("Failed to parse uri %s\n", coap_uri); - goto finish; - } - - /* resolve destination address where server should be sent */ - len = resolve_address(&uri.host, uri.port, &dst, 1 << uri.scheme); - if (len <= 0) { - coap_log_warn("Failed to resolve address %*.*s\n", (int)uri.host.length, (int)uri.host.length, - (const char *)uri.host.s); - goto finish; - } - - /* create CoAP context and a client session */ - if (!(ctx = coap_new_context(NULL))) { - coap_log_emerg("cannot create libcoap context\n"); - goto finish; - } - /* Support large responses */ - coap_context_set_block_mode(ctx, COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY); - - if (uri.scheme == COAP_URI_SCHEME_COAP) { - session = coap_new_client_session(ctx, NULL, &dst, COAP_PROTO_UDP); - } else if (uri.scheme == COAP_URI_SCHEME_COAP_TCP) { - session = coap_new_client_session(ctx, NULL, &dst, COAP_PROTO_TCP); - } - if (!session) { - coap_log_emerg("cannot create client session\n"); - goto finish; - } - - /* coap_register_response_handler(ctx, response_handler); */ - coap_register_response_handler(ctx, message_handler); - /* construct CoAP message */ - pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET, coap_new_message_id(session), - coap_session_max_pdu_size(session)); - if (!pdu) { - coap_log_emerg("cannot create PDU\n"); - goto finish; - } - - /* Add option list (which will be sorted) to the PDU */ - len = coap_uri_into_options(&uri, &dst, &optlist, 1, scratch, sizeof(scratch)); - if (len) { - coap_log_warn("Failed to create options\n"); - goto finish; - } - - if (optlist) { - res = coap_add_optlist_pdu(pdu, &optlist); - if (res != 1) { - coap_log_warn("Failed to add options to PDU\n"); - goto finish; - } - } - - coap_show_pdu(COAP_LOG_WARN, pdu); - - /* and send the PDU */ - if (coap_send(session, pdu) == COAP_INVALID_MID) { - coap_log_err("cannot send CoAP pdu\n"); - goto finish; - } - - wait_ms = (coap_session_get_default_leisure(session).integer_part + 1) * 1000; - - while (have_response == 0) { - res = coap_io_process(ctx, 1000); - if (res >= 0) { - if (wait_ms > 0) { - if ((unsigned)res >= wait_ms) { - printf("timeout\n"); - break; - } else { - wait_ms -= res; - } - } - } - } - - result = EXIT_SUCCESS; -finish: - coap_delete_optlist(optlist); - coap_session_release(session); - coap_free_context(ctx); - coap_cleanup(); - - return result; -} \ No newline at end of file diff --git a/test/platform/posix/coap_channel_client_test/run.sh b/test/platform/posix/coap_channel_client_test/run.sh deleted file mode 100755 index c9d4e0413..000000000 --- a/test/platform/posix/coap_channel_client_test/run.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# Make test -cmake -Bbuild -make -C build - -# Evaluate make output -if [ $? -eq 0 ]; then - echo "All tests passed." -else - echo "$? tests failed." >&2 - exit 1 -fi - -# Run test -./build/app - -# Evaluate test output -if [ $? -eq 0 ]; then - echo "All tests passed." -else - echo "$? tests failed." >&2 - exit 1 -fi \ No newline at end of file diff --git a/test/platform/posix/coap_channel_server_test/CMakeLists.txt b/test/platform/posix/coap_channel_server_test/CMakeLists.txt deleted file mode 100644 index b19f3794c..000000000 --- a/test/platform/posix/coap_channel_server_test/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -cmake_minimum_required(VERSION 3.20.0) -project(reactor-uc-posix-coap-channel-test) - -set(PLATFORM "POSIX" CACHE STRING "Platform to target") -add_subdirectory(../../../../ reactor-uc) - -add_executable(app main.c) - -target_link_libraries(app PRIVATE reactor-uc) \ No newline at end of file diff --git a/test/platform/posix/coap_channel_server_test/main.c b/test/platform/posix/coap_channel_server_test/main.c deleted file mode 100644 index 9f184dddc..000000000 --- a/test/platform/posix/coap_channel_server_test/main.c +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include -#include "coap3/coap.h" - -#define COAP_LISTEN_UCAST_IP "127.0.0.1" - -void resource_handler_hello(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response) { - const coap_address_t *remote_addr = coap_session_get_addr_remote(session); - - char addr_str[INET6_ADDRSTRLEN + 8] = {0}; - if (remote_addr) { - coap_print_addr(remote_addr, (uint8_t *)addr_str, sizeof(addr_str)); - printf("Request from: %s\n", addr_str); - } - coap_show_pdu(COAP_LOG_WARN, request); - coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); - coap_add_data(response, 5, (const uint8_t *)"world"); - coap_show_pdu(COAP_LOG_WARN, response); -} - -void resource_handler_hello_my(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response) { - coap_show_pdu(COAP_LOG_WARN, request); - coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); - coap_add_data(response, 8, (const uint8_t *)"my world"); - coap_show_pdu(COAP_LOG_WARN, response); -} - -int main(void) { - coap_context_t *ctx = NULL; - coap_resource_t *resource = NULL; - int result = EXIT_FAILURE; - - uint32_t scheme_hint_bits; - coap_addr_info_t *info = NULL; - coap_addr_info_t *info_list = NULL; - coap_str_const_t *my_address = coap_make_str_const(COAP_LISTEN_UCAST_IP); - bool have_ep = false; - - /* Initialize libcoap library */ - coap_startup(); - - /* Set logging level */ - coap_set_log_level(COAP_LOG_WARN); - - /* Create CoAP context */ - ctx = coap_new_context(NULL); - if (!ctx) { - coap_log_emerg("cannot initialize context\n"); - goto finish; - } - - /* Let libcoap do the multi-block payload handling (if any) */ - coap_context_set_block_mode(ctx, COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY); - - scheme_hint_bits = coap_get_available_scheme_hint_bits(0, 0, COAP_PROTO_UDP); - info_list = coap_resolve_address_info(my_address, 0, 0, 0, 0, 0, scheme_hint_bits, COAP_RESOLVE_TYPE_LOCAL); - /* Create CoAP listening endpoint(s) */ - for (info = info_list; info != NULL; info = info->next) { - coap_endpoint_t *ep; - - ep = coap_new_endpoint(ctx, &info->addr, info->proto); - if (!ep) { - coap_log_warn("cannot create endpoint for CoAP proto %u\n", info->proto); - } else { - have_ep = true; - } - } - coap_free_address_info(info_list); - if (have_ep == false) { - coap_log_err("No context available for interface '%s'\n", (const char *)my_address->s); - goto finish; - } - - /* Create a resource that the server can respond to with information */ - resource = coap_resource_init(coap_make_str_const("hello"), 0); - coap_register_handler(resource, COAP_REQUEST_GET, resource_handler_hello); - coap_add_resource(ctx, resource); - - /* Create another resource that the server can respond to with information */ - resource = coap_resource_init(coap_make_str_const("hello/my"), 0); - coap_register_handler(resource, COAP_REQUEST_GET, resource_handler_hello_my); - coap_add_resource(ctx, resource); - - /* Handle any libcoap I/O requirements */ - while (true) { - coap_io_process(ctx, COAP_IO_WAIT); - } - - result = EXIT_SUCCESS; -finish: - - coap_free_context(ctx); - coap_cleanup(); - - return result; -} \ No newline at end of file diff --git a/test/platform/posix/coap_channel_server_test/run.sh b/test/platform/posix/coap_channel_server_test/run.sh deleted file mode 100755 index c9d4e0413..000000000 --- a/test/platform/posix/coap_channel_server_test/run.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# Make test -cmake -Bbuild -make -C build - -# Evaluate make output -if [ $? -eq 0 ]; then - echo "All tests passed." -else - echo "$? tests failed." >&2 - exit 1 -fi - -# Run test -./build/app - -# Evaluate test output -if [ $? -eq 0 ]; then - echo "All tests passed." -else - echo "$? tests failed." >&2 - exit 1 -fi \ No newline at end of file From 264735a158b0b8f61b225c0d35e64c2f8b87f5fa Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Wed, 25 Jun 2025 18:34:30 +0200 Subject: [PATCH 23/24] WIP: Add full coap integration test with full reactor_uc runtime. --- CMakeLists.txt | 1 + src/platform/posix/coap_udp_ip_channel.c | 7 +- .../receiver/CMakeLists.txt | 12 ++ .../receiver/main.c | 113 +++++++++++++++++ .../posix/coap_channel_federated_test/run.sh | 96 ++++++++++++++ .../sender/CMakeLists.txt | 12 ++ .../coap_channel_federated_test/sender/main.c | 117 ++++++++++++++++++ .../posix/coap_channel_test/CMakeLists.txt | 14 +-- test/platform/posix/coap_channel_test/main.c | 2 +- 9 files changed, 358 insertions(+), 16 deletions(-) create mode 100644 test/platform/posix/coap_channel_federated_test/receiver/CMakeLists.txt create mode 100755 test/platform/posix/coap_channel_federated_test/receiver/main.c create mode 100755 test/platform/posix/coap_channel_federated_test/run.sh create mode 100644 test/platform/posix/coap_channel_federated_test/sender/CMakeLists.txt create mode 100755 test/platform/posix/coap_channel_federated_test/sender/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bd8391c3..e8e04c533 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ set(ASAN OFF CACHE BOOL "Compile with AddressSanitizer") set(PLATFORM "POSIX" CACHE STRING "Platform to target") set(SCHEDULER "DYNAMIC" CACHE STRING "Scheduler to use") set(NETWORK_CHANNEL_TCP_POSIX OFF CACHE BOOL "Use POSIX TCP NetworkChannel") +set(NETWORK_CHANNEL_COAP OFF CACHE BOOL "Use CoAP NetworkChannel") set(FEDERATED OFF CACHE BOOL "Compile with federated sources") # Code coverage setup diff --git a/src/platform/posix/coap_udp_ip_channel.c b/src/platform/posix/coap_udp_ip_channel.c index d8c2ba76f..008685f2e 100644 --- a/src/platform/posix/coap_udp_ip_channel.c +++ b/src/platform/posix/coap_udp_ip_channel.c @@ -505,9 +505,10 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_host, int // Let libcoap handle multi-block payloads coap_context_set_block_mode(_coap_context, COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY); - // Create CoAP listening endpoint(s) + // Create CoAP listening endpoint(s) - listen on all interfaces (0.0.0.0) int scheme_hint_bits = coap_get_available_scheme_hint_bits(0, 0, COAP_PROTO_UDP); - coap_addr_info_t *info_list = coap_resolve_address_info(NULL, COAP_DEFAULT_PORT, 0, 0, 0, AF_UNSPEC, + coap_str_const_t *listen_addr = coap_make_str_const("0.0.0.0"); + coap_addr_info_t *info_list = coap_resolve_address_info(listen_addr, COAP_DEFAULT_PORT, 0, 0, 0, AF_UNSPEC, scheme_hint_bits, COAP_RESOLVE_TYPE_LOCAL); bool endpoint_created = false; @@ -515,7 +516,7 @@ void CoapUdpIpChannel_ctor(CoapUdpIpChannel *self, const char *remote_host, int coap_endpoint_t *ep = coap_new_endpoint(_coap_context, &info->addr, info->proto); if (ep) { endpoint_created = true; - COAP_UDP_IP_CHANNEL_DEBUG("Created CoAP endpoint for protocol %u", info->proto); + COAP_UDP_IP_CHANNEL_DEBUG("Created CoAP endpoint for protocol %u on 0.0.0.0", info->proto); } else { COAP_UDP_IP_CHANNEL_WARN("Failed to create endpoint for protocol %u", info->proto); } diff --git a/test/platform/posix/coap_channel_federated_test/receiver/CMakeLists.txt b/test/platform/posix/coap_channel_federated_test/receiver/CMakeLists.txt new file mode 100644 index 000000000..c11883e45 --- /dev/null +++ b/test/platform/posix/coap_channel_federated_test/receiver/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.20.0) +project(reactor-uc-posix-coap-channel-test) + +set(NETWORK_CHANNEL_COAP ON CACHE BOOL "Use CoAP NetworkChannel") +set(FEDERATED ON CACHE BOOL "Compile with federated sources") +set(PLATFORM "POSIX" CACHE STRING "Platform to target") + +add_subdirectory(../../../../../ reactor-uc) + +add_executable(app main.c) + +target_link_libraries(app PRIVATE reactor-uc) diff --git a/test/platform/posix/coap_channel_federated_test/receiver/main.c b/test/platform/posix/coap_channel_federated_test/receiver/main.c new file mode 100755 index 000000000..ba88e6c1e --- /dev/null +++ b/test/platform/posix/coap_channel_federated_test/receiver/main.c @@ -0,0 +1,113 @@ +#define FEDERATED true +#define LOCAL_IP_ADDRESS "192.168.100.2" +#define REMOTE_IP_ADDRESS "192.168.100.1" + +#include "reactor-uc/platform/posix/coap_udp_ip_channel.h" +#include "reactor-uc/schedulers/dynamic/scheduler.h" +#include "reactor-uc/reactor-uc.h" + +#define REMOTE_PROTOCOL_FAMILY AF_INET6 + +typedef struct { + int size; + char msg[512]; +} lf_msg_t; + +lf_ret_t deserialize_msg_t(void *user_struct, const unsigned char *msg_buf, size_t msg_size) { + (void)msg_size; + + lf_msg_t *msg = user_struct; + memcpy(&msg->size, msg_buf, sizeof(msg->size)); + memcpy(msg->msg, msg_buf + sizeof(msg->size), msg->size); + + return LF_OK; +} + +LF_DEFINE_REACTION_STRUCT(Receiver, r, 0) +LF_DEFINE_REACTION_CTOR(Receiver, r, 0, NULL, NULL) +LF_DEFINE_INPUT_STRUCT(Receiver, in, 1, 0, lf_msg_t, 0) +LF_DEFINE_INPUT_CTOR(Receiver, in, 1, 0, lf_msg_t, 0) + +typedef struct { + Reactor super; + LF_REACTION_INSTANCE(Receiver, r); + LF_PORT_INSTANCE(Receiver, in, 1); + int cnt; + LF_REACTOR_BOOKKEEPING_INSTANCES(1, 1, 0); +} Receiver; + +LF_DEFINE_REACTION_BODY(Receiver, r) { + LF_SCOPE_SELF(Receiver); + LF_SCOPE_ENV(); + LF_SCOPE_PORT(Receiver, in); + printf("Input triggered @ " PRINTF_TIME " with %s size %d\n", env->get_elapsed_logical_time(env), in->value.msg, + in->value.size); + + if (strcmp(in->value.msg, "Hello From Sender") == 0) { + // Exit with 0 to show that the test passed. + exit(0); + } else { + exit(1); + } +} + +LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Receiver, InputExternalCtorArgs *in_external) { + LF_REACTOR_CTOR_PREAMBLE(); + LF_REACTOR_CTOR(Receiver); + LF_INITIALIZE_REACTION(Receiver, r, NEVER); + LF_INITIALIZE_INPUT(Receiver, in, 1, in_external); + + // Register reaction as an effect of in + LF_PORT_REGISTER_EFFECT(self->in, self->r, 1); +} + +LF_DEFINE_FEDERATED_INPUT_CONNECTION_STRUCT(Receiver, in, lf_msg_t, 5); +LF_DEFINE_FEDERATED_INPUT_CONNECTION_CTOR(Receiver, in, lf_msg_t, 5, MSEC(100), false, 0); + +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_TYPE(Receiver, Sender); + +LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Receiver, Sender) { + LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); + CoapUdpIpChannel_ctor(&self->channel, REMOTE_IP_ADDRESS, REMOTE_PROTOCOL_FAMILY); + LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); + LF_INITIALIZE_FEDERATED_INPUT_CONNECTION(Receiver, in, deserialize_msg_t); +} + +LF_DEFINE_STARTUP_COORDINATOR_STRUCT(Federate, 1, 6); +LF_DEFINE_STARTUP_COORDINATOR_CTOR(Federate, 1, 1, 6); + +LF_DEFINE_CLOCK_SYNC_STRUCT(Federate, 1, 3); +// LF_DEFINE_CLOCK_SYNC_DEFAULTS_CTOR(Federate, 1, 3, false); + +typedef struct { + Reactor super; + LF_CHILD_REACTOR_INSTANCE(Receiver, receiver, 1); + LF_FEDERATED_CONNECTION_BUNDLE_INSTANCE(Receiver, Sender); + LF_FEDERATE_BOOKKEEPING_INSTANCES(1); + LF_CHILD_INPUT_SOURCES(receiver, in, 1, 1, 0); + LF_DEFINE_STARTUP_COORDINATOR(Federate); + LF_DEFINE_CLOCK_SYNC(Federate); +} MainRecv; + +LF_REACTOR_CTOR_SIGNATURE(MainRecv) { + LF_REACTOR_CTOR(MainRecv); + LF_FEDERATE_CTOR_PREAMBLE(); + 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_connect_federated_input(&self->Receiver_Sender_bundle.inputs[0]->super, &self->receiver->in[0].super); + LF_INITIALIZE_STARTUP_COORDINATOR(Federate); + // LF_INITIALIZE_CLOCK_SYNC(Federate); +} + +LF_ENTRY_POINT_FEDERATED(MainRecv, 32, 32, 32, SEC(1), true, 1, false) + +int main() { + lf_start(); + return 0; +} diff --git a/test/platform/posix/coap_channel_federated_test/run.sh b/test/platform/posix/coap_channel_federated_test/run.sh new file mode 100755 index 000000000..3ed1ee128 --- /dev/null +++ b/test/platform/posix/coap_channel_federated_test/run.sh @@ -0,0 +1,96 @@ +#!/bin/bash +set -e + +# --------------------- Networking setup --------------------- + +# Cleanup network if it exists already +sudo ip netns del sender_ns || true +sudo ip netns del receiver_ns || true + +# 1. Create network namespaces +echo "Setting up network namespaces..." +sudo ip netns add sender_ns +sudo ip netns add receiver_ns + +# Create virtual ethernet pair +sudo ip link add veth_sender type veth peer name veth_receiver + +# Move interfaces to namespaces +sudo ip link set veth_sender netns sender_ns +sudo ip link set veth_receiver netns receiver_ns + +# Configure IP addresses +sudo ip netns exec sender_ns ip addr add 192.168.100.1/24 dev veth_sender +sudo ip netns exec receiver_ns ip addr add 192.168.100.2/24 dev veth_receiver + +# Bring up interfaces +sudo ip netns exec sender_ns ip link set veth_sender up +sudo ip netns exec receiver_ns ip link set veth_receiver up +sudo ip netns exec sender_ns ip link set lo up +sudo ip netns exec receiver_ns ip link set lo up + +echo "Network setup complete. Building applications..." + +# Test connectivity +# sudo ip netns exec sender_ns ping 192.168.100.2 +# sudo ip netns exec receiver_ns ping 192.168.100.1 + + +# --------------------- BUILD AND RUN --------------------- + +# Build sender +cmake -B sender/build -S sender +make -C sender/build + +# Build receiver +cmake -B receiver/build -S receiver +make -C receiver/build + +echo "Building complete. Starting applications..." + +# Start the sender with a 60-second timeout in sender namespace +sudo ip netns exec sender_ns timeout 60 ./sender/build/app & +pid1=$! + +# Wait for 3 seconds +sleep 3 + +# Start the receiver with a 60-second timeout in receiver namespace +sudo ip netns exec receiver_ns timeout 60 ./receiver/build/app & +pid2=$! + +# Wait for both binaries to complete or timeout +wait $pid1 +exit_code_sender=$? + +wait $pid2 +exit_code_receiver=$? + +echo "Test completed" +echo "Sender exit code: $exit_code_sender" +echo "Receiver exit code: $exit_code_receiver" + +# Check if timeout occurred +if [ $exit_code_sender -eq 124 ]; then + echo "Error sender timed out" + echo "Test failed" + exit 1 +else + echo "Exit code of sender: $exit_code_sender" +fi + +if [ $exit_code_receiver -eq 124 ]; then + echo "Error receiver timed out" + echo "Test failed" + exit 1 +else + echo "Exit code of receiver: $exit_code_receiver" +fi + +# Check exit code +if [[ $exit_code_receiver -ne 0 ]]; then + echo "Error: Receiver received wrong message text" + exit 1 +fi + +echo "All tests passed." diff --git a/test/platform/posix/coap_channel_federated_test/sender/CMakeLists.txt b/test/platform/posix/coap_channel_federated_test/sender/CMakeLists.txt new file mode 100644 index 000000000..c11883e45 --- /dev/null +++ b/test/platform/posix/coap_channel_federated_test/sender/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.20.0) +project(reactor-uc-posix-coap-channel-test) + +set(NETWORK_CHANNEL_COAP ON CACHE BOOL "Use CoAP NetworkChannel") +set(FEDERATED ON CACHE BOOL "Compile with federated sources") +set(PLATFORM "POSIX" CACHE STRING "Platform to target") + +add_subdirectory(../../../../../ reactor-uc) + +add_executable(app main.c) + +target_link_libraries(app PRIVATE reactor-uc) diff --git a/test/platform/posix/coap_channel_federated_test/sender/main.c b/test/platform/posix/coap_channel_federated_test/sender/main.c new file mode 100755 index 000000000..d21112880 --- /dev/null +++ b/test/platform/posix/coap_channel_federated_test/sender/main.c @@ -0,0 +1,117 @@ +#define FEDERATED 1 +#define LOCAL_IP_ADDRESS "192.168.100.1" +#define REMOTE_IP_ADDRESS "192.168.100.2" + +#include "reactor-uc/reactor-uc.h" +#include "reactor-uc/schedulers/dynamic/scheduler.h" +#include "reactor-uc/platform/posix/coap_udp_ip_channel.h" + +#define REMOTE_PROTOCOL_FAMILY AF_INET6 + +typedef struct { + int size; + char msg[512]; +} lf_msg_t; + +int serialize_msg_t(const void *user_struct, size_t user_struct_size, unsigned char *msg_buf) { + (void)user_struct_size; + const lf_msg_t *msg = user_struct; + + memcpy(msg_buf, &msg->size, sizeof(msg->size)); + memcpy(msg_buf + sizeof(msg->size), msg->msg, msg->size); + + return sizeof(msg->size) + msg->size; +} + +LF_DEFINE_TIMER_STRUCT(Sender, t, 1, 0) +LF_DEFINE_TIMER_CTOR(Sender, t, 1, 0) +LF_DEFINE_REACTION_STRUCT(Sender, r, 1) +LF_DEFINE_REACTION_CTOR(Sender, r, 0, NULL, NULL) +LF_DEFINE_OUTPUT_STRUCT(Sender, out, 1, lf_msg_t) +LF_DEFINE_OUTPUT_CTOR(Sender, out, 1) + +typedef struct { + Reactor super; + LF_TIMER_INSTANCE(Sender, t); + LF_REACTION_INSTANCE(Sender, r); + LF_PORT_INSTANCE(Sender, out, 1); + LF_REACTOR_BOOKKEEPING_INSTANCES(1, 2, 0); +} Sender; + +LF_DEFINE_REACTION_BODY(Sender, r) { + LF_SCOPE_SELF(Sender); + LF_SCOPE_ENV(); + LF_SCOPE_PORT(Sender, out); + + printf("Timer triggered @ " PRINTF_TIME "\n", env->get_elapsed_logical_time(env)); + lf_msg_t val; + strcpy(val.msg, "Hello From Sender"); + val.size = sizeof("Hello From Sender"); + lf_set(out, val); +} + +LF_REACTOR_CTOR_SIGNATURE_WITH_PARAMETERS(Sender, OutputExternalCtorArgs *out_external) { + LF_REACTOR_CTOR_PREAMBLE(); + LF_REACTOR_CTOR(Sender); + LF_INITIALIZE_REACTION(Sender, r, NEVER); + LF_INITIALIZE_TIMER(Sender, t, MSEC(0), SEC(1)); + LF_INITIALIZE_OUTPUT(Sender, out, 1, out_external); + + LF_TIMER_REGISTER_EFFECT(self->t, self->r); + LF_PORT_REGISTER_SOURCE(self->out, self->r, 1); +} + +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_STRUCT(Sender, out, lf_msg_t) +LF_DEFINE_FEDERATED_OUTPUT_CONNECTION_CTOR(Sender, out, lf_msg_t, 0) + +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_TYPE(Sender, Receiver); + +LF_FEDERATED_CONNECTION_BUNDLE_CTOR_SIGNATURE(Sender, Receiver) { + LF_FEDERATED_CONNECTION_BUNDLE_CTOR_PREAMBLE(); + CoapUdpIpChannel_ctor(&self->channel, REMOTE_IP_ADDRESS, REMOTE_PROTOCOL_FAMILY); + LF_FEDERATED_CONNECTION_BUNDLE_CALL_CTOR(); + LF_INITIALIZE_FEDERATED_OUTPUT_CONNECTION(Sender, out, serialize_msg_t); +} + +LF_DEFINE_STARTUP_COORDINATOR_STRUCT(Federate, 1, 6); +LF_DEFINE_STARTUP_COORDINATOR_CTOR(Federate, 1, 1, 6); + +LF_DEFINE_CLOCK_SYNC_STRUCT(Federate, 1, 3); +// LF_DEFINE_CLOCK_SYNC_DEFAULTS_CTOR(Federate, 1, 3, true); + +// Reactor main +typedef struct { + Reactor super; + LF_CHILD_REACTOR_INSTANCE(Sender, sender, 1); + LF_FEDERATED_CONNECTION_BUNDLE_INSTANCE(Sender, Receiver); + LF_FEDERATE_BOOKKEEPING_INSTANCES(1); + LF_CHILD_OUTPUT_CONNECTIONS(sender, out, 1, 1, 1); + LF_CHILD_OUTPUT_EFFECTS(sender, out, 1, 1, 0); + LF_CHILD_OUTPUT_OBSERVERS(sender, out, 1, 1, 0); + LF_DEFINE_STARTUP_COORDINATOR(Federate); + LF_DEFINE_CLOCK_SYNC(Federate); +} MainSender; + +LF_REACTOR_CTOR_SIGNATURE(MainSender) { + LF_REACTOR_CTOR(MainSender); + LF_FEDERATE_CTOR_PREAMBLE(); + 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_INITIALIZE_STARTUP_COORDINATOR(Federate); + // LF_INITIALIZE_CLOCK_SYNC(Federate); + lf_connect_federated_output((Connection *)self->Sender_Receiver_bundle.outputs[0], (Port *)self->sender->out); +} + +LF_ENTRY_POINT_FEDERATED(MainSender, 32, 32, 32, SEC(1), true, 1, false) + +int main() { + lf_start(); + exit(0); + return 0; +} diff --git a/test/platform/posix/coap_channel_test/CMakeLists.txt b/test/platform/posix/coap_channel_test/CMakeLists.txt index 347d5eee2..a3ac798ff 100644 --- a/test/platform/posix/coap_channel_test/CMakeLists.txt +++ b/test/platform/posix/coap_channel_test/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.20.0) project(reactor-uc-posix-coap-channel-test) -set(FEDERATED ON) -set(NETWORK_CHANNEL_COAP ON) +set(NETWORK_CHANNEL_COAP ON CACHE BOOL "Use CoAP NetworkChannel") +set(FEDERATED ON CACHE BOOL "Compile with federated sources") set(PLATFORM "POSIX" CACHE STRING "Platform to target") add_subdirectory(../../../../ reactor-uc) @@ -10,13 +10,3 @@ add_subdirectory(../../../../ reactor-uc) add_executable(app main.c) target_link_libraries(app PRIVATE reactor-uc) - - - - - - - - - - diff --git a/test/platform/posix/coap_channel_test/main.c b/test/platform/posix/coap_channel_test/main.c index e5342fc9f..64bf09c86 100644 --- a/test/platform/posix/coap_channel_test/main.c +++ b/test/platform/posix/coap_channel_test/main.c @@ -1,4 +1,4 @@ -#define FEDERATED 1 +#define FEDERATED true #include "reactor-uc/platform/posix/coap_udp_ip_channel.h" #include "reactor-uc/reactor-uc.h" From efccceefa999bcf46f59f49f2041a2bb5a2e62b3 Mon Sep 17 00:00:00 2001 From: Lasse Rosenow Date: Wed, 25 Jun 2025 18:35:10 +0200 Subject: [PATCH 24/24] Small cleanup --- test/platform/posix/coap_channel_federated_test/receiver/main.c | 1 - test/platform/posix/coap_channel_federated_test/sender/main.c | 1 - 2 files changed, 2 deletions(-) diff --git a/test/platform/posix/coap_channel_federated_test/receiver/main.c b/test/platform/posix/coap_channel_federated_test/receiver/main.c index ba88e6c1e..de59a0e17 100755 --- a/test/platform/posix/coap_channel_federated_test/receiver/main.c +++ b/test/platform/posix/coap_channel_federated_test/receiver/main.c @@ -1,5 +1,4 @@ #define FEDERATED true -#define LOCAL_IP_ADDRESS "192.168.100.2" #define REMOTE_IP_ADDRESS "192.168.100.1" #include "reactor-uc/platform/posix/coap_udp_ip_channel.h" diff --git a/test/platform/posix/coap_channel_federated_test/sender/main.c b/test/platform/posix/coap_channel_federated_test/sender/main.c index d21112880..d78c6e8eb 100755 --- a/test/platform/posix/coap_channel_federated_test/sender/main.c +++ b/test/platform/posix/coap_channel_federated_test/sender/main.c @@ -1,5 +1,4 @@ #define FEDERATED 1 -#define LOCAL_IP_ADDRESS "192.168.100.1" #define REMOTE_IP_ADDRESS "192.168.100.2" #include "reactor-uc/reactor-uc.h"