diff --git a/aether/channels/ethernet_channel.cpp b/aether/channels/ethernet_channel.cpp index f5dd2460..3618d941 100644 --- a/aether/channels/ethernet_channel.cpp +++ b/aether/channels/ethernet_channel.cpp @@ -65,6 +65,12 @@ ex::sender auto TransportConnect(std::unique_ptr&& stream) { ex::set_error_t(int)>( [&, s{std::move(stream)}, link_sub{Subscription{}}](auto& ctx) mutable noexcept { + if (!s) { + // Protocol compiled out (AE_SUPPORT_* = 0) — factory returns null. + ex::set_error(std::move(ctx.receiver), 1); + return; + } + auto handle_link_state = [&]() noexcept { link_sub.Reset(); switch (s->stream_info().link_state) { diff --git a/aether/channels/wifi_channel.cpp b/aether/channels/wifi_channel.cpp index 7eb095a7..14ff94af 100644 --- a/aether/channels/wifi_channel.cpp +++ b/aether/channels/wifi_channel.cpp @@ -85,6 +85,12 @@ ex::sender auto TransportConnect(std::unique_ptr&& stream) { ex::set_error_t(int)>( [s{std::move(stream)}, link_sub{Subscription{}}](auto& ctx) mutable noexcept { + if (!s) { + // Protocol compiled out (AE_SUPPORT_* = 0) — factory returns null. + ex::set_error(std::move(ctx.receiver), 1); + return; + } + auto handle_link_state = [&]() noexcept { link_sub.Reset(); // ensure subscribed only to one event auto link_state = s->stream_info().link_state; diff --git a/aether/server_connections/server_connection.cpp b/aether/server_connections/server_connection.cpp index 8afc277d..d52e81b2 100644 --- a/aether/server_connections/server_connection.cpp +++ b/aether/server_connections/server_connection.cpp @@ -21,7 +21,9 @@ #include "aether/aether.h" #include "aether/channels/channel.h" +#include "aether/config.h" #include "aether/server.h" +#include "aether/types/address.h" #include "aether/tele.h" @@ -94,7 +96,25 @@ void ServerConnection::InitChannels() { for (auto c : server->channels) { // channel must be loaded before use assert(c.is_valid() && "Channel is not loaded"); - channels.emplace_back(c.Load()); + auto channel = c.Load(); + // Skip channels whose transport was compiled out (e.g. UDP-only state + // loaded into a TCP-only build). Factory returns nullptr for those and + // the connection would never link. + if (auto endpoint = channel->endpoint()) { +#if !AE_SUPPORT_TCP + if (endpoint->protocol == Protocol::kTcp) { + AE_TELED_DEBUG("Skip unsupported TCP channel {}", *endpoint); + continue; + } +#endif +#if !AE_SUPPORT_UDP + if (endpoint->protocol == Protocol::kUdp) { + AE_TELED_DEBUG("Skip unsupported UDP channel {}", *endpoint); + continue; + } +#endif + } + channels.emplace_back(std::move(channel)); } // sort channels by the fastest std::sort(std::begin(channels), std::end(channels), diff --git a/examples/a_b_message_exchange/CMakeLists.txt b/examples/a_b_message_exchange/CMakeLists.txt index 9683a558..7485e827 100644 --- a/examples/a_b_message_exchange/CMakeLists.txt +++ b/examples/a_b_message_exchange/CMakeLists.txt @@ -26,6 +26,11 @@ if(NOT CM_PLATFORM) else() idf_build_get_property(CM_PLATFORM CM_PLATFORM) if(CM_PLATFORM STREQUAL "ESP32") + # TCP-only ping-pong config unless the build already selected one. + if("${USER_CONFIG}" STREQUAL "") + set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/user_config.h" + CACHE PATH "Path to user provided configuration header file" FORCE) + endif() idf_component_register( SRCS main.cpp a_b_message_exchange.cpp INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ../common diff --git a/examples/a_b_message_exchange/user_config.h b/examples/a_b_message_exchange/user_config.h new file mode 100644 index 00000000..54cb4937 --- /dev/null +++ b/examples/a_b_message_exchange/user_config.h @@ -0,0 +1,75 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EXAMPLES_A_B_MESSAGE_EXCHANGE_USER_CONFIG_H_ +#define EXAMPLES_A_B_MESSAGE_EXCHANGE_USER_CONFIG_H_ + +#include "aether/config_consts.h" + +/** + * \brief For full config list and default values \see aether/config.h + * + * Desktop ping-pong (ab-message-exchange): force TCP-only cloud/server + * channels. Without this, connectionless UDP channels sort first and are + * preferred over TCP. + */ + +#define AE_CRYPTO_ASYNC AE_HYDRO_CRYPTO_PK +#define AE_CRYPTO_SYNC AE_HYDRO_CRYPTO_SK +#define AE_SIGNATURE AE_HYDRO_SIGNATURE +#define AE_KDF AE_HYDRO_KDF + +#define AE_SUPPORT_UDP 0 +#define AE_SUPPORT_TCP 1 + +#if !ESP_PLATFORM +# define AE_SUPPORT_WIFIS 0 +#endif + +#define AE_TELE_ENABLED 1 +#define AE_TELE_LOG_CONSOLE 1 +#define AE_TELE_COMPILATION_INFO 1 +#define AE_TELE_LOG_TO_STATISTICS 1 +#define AE_STATISTICS_MAX_SIZE 1024 + +#define AE_TELE_METRICS_MODULES_EXCLUDE \ + { AE_LOG_MODULE } +#define AE_TELE_METRICS_DURATION_EXCLUDE \ + { AE_LOG_MODULE } + +#define AE_TELE_LOG_MODULES AE_ALL +#define AE_TELE_DEBUG_MODULES AE_ALL +#define AE_TELE_INFO_MODULES AE_ALL +#define AE_TELE_WARN_MODULES AE_ALL +#define AE_TELE_ERROR_MODULES AE_ALL + +#define AE_TELE_LOG_TIME_POINT AE_ALL +#define AE_TELE_LOG_LOCATION \ + { AE_LOG_MODULE } +#define AE_TELE_LOG_NAME_EXCLUDE \ + { AE_LOG_MODULE } +#define AE_TELE_LOG_LEVEL_MODULE AE_ALL +#define AE_TELE_LOG_BLOB AE_ALL + +#if AE_DISTILLATION || AE_FILTRATION +# define AE_SUPPORT_REGISTRATION 1 +# define AE_SUPPORT_CLOUD_DNS 1 +#else +# define AE_SUPPORT_REGISTRATION 0 +# define AE_SUPPORT_CLOUD_DNS 0 +#endif + +#endif // EXAMPLES_A_B_MESSAGE_EXCHANGE_USER_CONFIG_H_