diff --git a/.github/workflows/ci-cd-multi-platforms.yml b/.github/workflows/ci-cd-multi-platforms.yml index 997253ea..8b6b60bd 100644 --- a/.github/workflows/ci-cd-multi-platforms.yml +++ b/.github/workflows/ci-cd-multi-platforms.yml @@ -109,14 +109,12 @@ jobs: cmake -B build -G "${{ matrix.config.generator }}" -A "${{ matrix.config.arch }}" - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_COMPILER=${{ matrix.config.cxx }} -DCMAKE_C_COMPILER=${{ matrix.config.cc }} -DCMAKE_BUILD_TYPE=Release -DUSER_CONFIG="${{ matrix.platforms.user_config }}" - -DAE_BUILD_TESTS=On -C "${{ matrix.config.initial_cache }}" - -S projects/cmake + -S ./ - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). diff --git a/.github/workflows/ci-cppcheck.yml b/.github/workflows/ci-cppcheck.yml index ba539dd8..625aa164 100644 --- a/.github/workflows/ci-cppcheck.yml +++ b/.github/workflows/ci-cppcheck.yml @@ -13,26 +13,26 @@ jobs: cppcheck: runs-on: ubuntu-latest steps: - - name: configure system - run: | - sudo apt-get update - sudo apt-get install -y make ninja-build + - name: configure system + run: | + sudo apt-get update + sudo apt-get install -y make ninja-build - - uses: actions/checkout@v4 - with: + - uses: actions/checkout@v4 + with: submodules: recursive - - name: cache cppcheck - id: cache-cppcheck - uses: actions/cache@v4 - with: - path: _cppcheck - key: ${{ runner.os }}-cppcheck-2.19.0 - restore-keys: ${{ runner.os }}-cppcheck-2.19.0 + - name: cache cppcheck + id: cache-cppcheck + uses: actions/cache@v4 + with: + path: _cppcheck + key: ${{ runner.os }}-cppcheck-2.19.0 + restore-keys: ${{ runner.os }}-cppcheck-2.19.0 - - if: ${{ steps.cache-cppcheck.outputs.cache-hit != 'true' }} - name: download and build cppcheck - run: | + - if: ${{ steps.cache-cppcheck.outputs.cache-hit != 'true' }} + name: download and build cppcheck + run: | mkdir -p _cppcheck wget -O _cppcheck/cppcheck.tar.gz https://github.com/danmar/cppcheck/archive/refs/tags/2.19.0.tar.gz cd _cppcheck @@ -45,43 +45,43 @@ jobs: cd ../.. _cppcheck/cppcheck-2.19.0/cppcheck --version - # we need configure cmake to get the compile_commands.json file - - name: cmake configure - run: > - cmake -B build - -G Ninja - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - -DCMAKE_BUILD_TYPE=Release - -C ".github/workflows/linux_initial_cache.txt" - -S projects/cmake + # we need configure cmake to get the compile_commands.json file + - name: cmake configure + run: > + cmake -B build + -G Ninja + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + -DCMAKE_BUILD_TYPE=Release + -C ".github/workflows/linux_initial_cache.txt" + -S ./ - - name: cache cppcheck_build_dir - id: cache-cppcheck_build_dir - uses: actions/cache@v4 - with: - path: _cppcheck_build_dir - key: ${{ runner.os }}-cppcheck_build_dir - restore-keys: ${{ runner.os }}-cppcheck_build_dir + - name: cache cppcheck_build_dir + id: cache-cppcheck_build_dir + uses: actions/cache@v4 + with: + path: _cppcheck_build_dir + key: ${{ runner.os }}-cppcheck_build_dir + restore-keys: ${{ runner.os }}-cppcheck_build_dir - # _cppcheck_build_dir is required to increase cppcheck check speed - - if: ${{ steps.cache-cppcheck_build_dir.outputs.cache-hit != 'true' }} - name: cppcheck_build_dir - run: mkdir -p _cppcheck_build_dir + # _cppcheck_build_dir is required to increase cppcheck check speed + - if: ${{ steps.cache-cppcheck_build_dir.outputs.cache-hit != 'true' }} + name: cppcheck_build_dir + run: mkdir -p _cppcheck_build_dir - - name: cppcheck - run: > - ./_cppcheck/cppcheck-2.19.0/cppcheck --project=build/compile_commands.json - -D__GNUC__=4 - --safety - --error-exitcode=-1 - -i "third_party/*" - --enable=warning,performance,portability,missingInclude - --suppressions-list=./cppcheck_suppressions.txt - --inline-suppr - --quiet - -j $(nproc) - --cppcheck-build-dir=./_cppcheck_build_dir - --checkers-report=./checkers.txt + - name: cppcheck + run: > + cd build && + ../_cppcheck/cppcheck-2.19.0/cppcheck --project=compile_commands.json + -D__GNUC__=4 + --safety + --error-exitcode=-1 + --enable=warning,performance,portability,missingInclude + --suppressions-list=../cppcheck_suppressions.txt + --inline-suppr + --quiet + -j $(nproc) + --cppcheck-build-dir=../_cppcheck_build_dir + --checkers-report=../checkers.txt - - name: print checkers report - run: cat checkers.txt + - name: print checkers report + run: cat checkers.txt diff --git a/.gitmodules b/.gitmodules index 27a1e12a..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,62 +0,0 @@ -[submodule "third_party/base-n"] - path = third_party/base-n - url = https://github.com/azawadzki/base-n.git - branch = master - ignore = dirty - -[submodule "third_party/libbcrypt"] - path = third_party/libbcrypt - url = https://github.com/rg3/libbcrypt.git - branch = master - ignore = dirty - -[submodule "third_party/libhydrogen"] - path = third_party/libhydrogen - url = https://github.com/jedisct1/libhydrogen.git - branch = master - ignore = dirty - -[submodule "third_party/libsodium"] - path = third_party/libsodium - url = https://github.com/jedisct1/libsodium.git - branch = master - ignore = dirty - -[submodule "third_party/Unity"] - path = third_party/Unity - url = https://github.com/ThrowTheSwitch/Unity.git - branch = master - ignore = dirty - -[submodule "third_party/c-ares"] - path = third_party/c-ares - url = https://github.com/c-ares/c-ares.git - branch = main - ignore = dirty - -[submodule "third_party/date"] - path = third_party/date - url = https://github.com/HowardHinnant/date.git - branch = master - ignore = dirty - -[submodule "third_party/ini.h"] - path = third_party/ini.h - url = https://github.com/giosali/ini.h - branch = main - ignore = dirty - -[submodule "third_party/etl"] - path = third_party/etl - url = https://github.com/ETLCPP/etl.git - branch = master - ignore = dirty - -[submodule "third_party/aethernet-numeric"] - path = third_party/aethernet-numeric - url = https://github.com/aethernetio/aethernet-numeric.git - branch = main -[submodule "third_party/gcem"] - path = third_party/gcem - url = https://github.com/aethernetio/gcem.git - branch = master diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..ebe99d72 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,390 @@ +# 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. + +cmake_minimum_required(VERSION 3.16.0) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(AE_PROJECT_VERSION "0.1.0") + +project(aether VERSION ${AE_PROJECT_VERSION} LANGUAGES CXX C) + +set(TARGET_NAME "${PROJECT_NAME}") + +if("${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") + set(AE_ROOT_PORJECT On) +else() + set(AE_ROOT_PORJECT Off) +endif() + +option(AE_DISTILLATION "Build aether in distillation mode" On) +option(AE_FILTRATION "Build aether in filtration mode" Off) +option(AE_INSTALL "Install aether" ${AE_ROOT_PORJECT}) +option(AE_BUILD_TOOLS "Build tools" ${AE_ROOT_PORJECT}) +option(AE_BUILD_EXAMPLES "Build examples" ${AE_ROOT_PORJECT}) +option(AE_BUILD_TESTS "Build tests" ${AE_ROOT_PORJECT}) +option(AE_ADDRESS_SANITIZE "Enable address sanitizer" Off) +option(AE_NO_STRIP_ALL "Do not apply --strip_all, useful for bloaty and similar tools " Off) + +set(UTM_ID "0" CACHE STRING "User Tracking Measurement ID, must be a uint32 value") +set(USER_CONFIG "" CACHE PATH "Path to user provided configuration header file") +set(FS_INIT "" CACHE PATH "Path to user provided saved state header file") +set(AE_REG_CLOUD_ADDR "" CACHE STRING "Address of the registration cloud") + +message(STATUS "Aether build options: + AE_ROOT_PORJECT=${AE_ROOT_PORJECT} + AE_DISTILLATION=${AE_DISTILLATION} + AE_FILTRATION=${AE_FILTRATION} + AE_INSTALL=${AE_INSTALL} + AE_BUILD_TOOLS=${AE_BUILD_TOOLS} + AE_BUILD_EXAMPLES=${AE_BUILD_EXAMPLES} + AE_BUILD_TESTS=${AE_BUILD_TESTS} + AE_ADDRESS_SANITIZE=${AE_ADDRESS_SANITIZE} + AE_NO_STRIP_ALL=${AE_NO_STRIP_ALL} + UTM_ID=${UTM_ID} + USER_CONFIG=${USER_CONFIG} + FS_INIT=${FS_INIT} + AE_REG_CLOUD_ADDR=${AE_REG_CLOUD_ADDR} +") + +include(cmake/CPM.cmake) + +# For using CPM.cmake projects with external package managers, such as conan or vcpkg, +# setting the variable CPM_USE_LOCAL_PACKAGES will make CPM.cmake try to add a package through +# find_package first, and add it from source if it doesn't succeed. +# It is possible to override the consumer's dependency with the version by supplying +# the CMake option CPM__SOURCE set to the absolute path of the local library. +# All dependencies must be EXCLUDE_FROM_ALL FALSE to make them install together with the aether. +# To avoid extra downloads it is recommend to set the CPM_SOURCE_CACHE environmental variable +# Also it required to used PATCHES +# +if (NOT CPM_SOURCE_CACHE) + set(CPM_SOURCE_CACHE "${CMAKE_CURRENT_BINARY_DIR}/cpm.cache") +endif() + +CPMAddPackage( + NAME libbcrypt + GIT_REPOSITORY "https://github.com/rg3/libbcrypt.git" + GIT_TAG "master" + PATCHES "${CMAKE_CURRENT_LIST_DIR}/third_party/libbcrypt.patch" + OPTIONS "ENABLE_INSTALL ${AE_INSTALL}" + EXCLUDE_FROM_ALL FALSE +) +# TODO: libhydrogen newer than bbca575 is not compatible with used in aether server +CPMAddPackage( + NAME libhydrogen + GIT_REPOSITORY "https://github.com/jedisct1/libhydrogen.git" + GIT_TAG "bbca575" + PATCHES "${CMAKE_CURRENT_LIST_DIR}/third_party/libhydrogen.patch" + OPTIONS "ENABLE_INSTALL ${AE_INSTALL}" + EXCLUDE_FROM_ALL FALSE +) +CPMAddPackage( + NAME libsodium + GIT_REPOSITORY "https://github.com/jedisct1/libsodium.git" + GIT_TAG "master" + PATCHES "${CMAKE_CURRENT_LIST_DIR}/third_party/libsodium.patch" "${CMAKE_CURRENT_LIST_DIR}/third_party/libsodium_cmake.patch" + OPTIONS "ENABLE_INSTALL ${AE_INSTALL}" + EXCLUDE_FROM_ALL FALSE +) +CPMAddPackage( + NAME gcem + GIT_REPOSITORY "https://github.com/aethernetio/gcem.git" + GIT_TAG "master" + EXCLUDE_FROM_ALL FALSE +) +# TODO: remove date +CPMAddPackage( + URI "https://github.com/HowardHinnant/date.git#master" + OPTIONS "ENABLE_DATE_INSTALL ${AE_INSTALL}" + EXCLUDE_FROM_ALL FALSE +) +CPMAddPackage( + NAME numeric + GIT_REPOSITORY "https://github.com/aethernetio/aethernet-numeric.git" + GIT_TAG "main" + OPTIONS "AE_NUMERIC_INSTALL ${AE_INSTALL}" + EXCLUDE_FROM_ALL FALSE +) +CPMAddPackage( + NAME etl + GIT_REPOSITORY "https://github.com/ETLCPP/etl.git" + GIT_TAG "20.44.2" + OPTIONS "GIT_DIR_LOOKUP_POLICY ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR" "ENABLE_INSTALL ${AE_INSTALL}" + PATCHES "${CMAKE_CURRENT_LIST_DIR}/third_party/etl.patch" + EXCLUDE_FROM_ALL FALSE +) +CPMAddPackage( + NAME stdexec + GIT_REPOSITORY "https://github.com/aethernetio/stdexec.git" +# TODO: switch to main + GIT_TAG "2091-compilation-failed-with-fno-exception" + OPTIONS "STDEXEC_BUILD_EXAMPLES OFF" "STDEXEC_INSTALL ${AE_INSTALL}" + EXCLUDE_FROM_ALL FALSE +) + +add_library(${TARGET_NAME} STATIC ) +# collect sources +add_subdirectory(aether) + +# https://stackoverflow.com/questions/25676277/cmake-target-include-directories-prints-an-error-when-i-try-to-add-the-source +target_include_directories(${TARGET_NAME} PUBLIC + $ + $ +) +target_link_libraries(${TARGET_NAME} PUBLIC + bcrypt + sodium + hydrogen + gcem + etl + stdexec + numeric + date) + +message(STATUS "Aether build for CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" + OR CMAKE_SYSTEM_NAME STREQUAL "Darwin" + OR CMAKE_SYSTEM_NAME MATCHES ".*BSD.*" + OR CMAKE_SYSTEM_NAME STREQUAL "Windows" ) + # for desktop projects add c-ares + CPMAddPackage( + NAME c-ares + GIT_REPOSITORY "https://github.com/c-ares/c-ares.git" + GIT_TAG "main" + OPTIONS "CARES_BUILD_TOOLS OFF" + "CARES_STATIC ON" + "CARES_SHARED OFF" + "CARES_INSTALL ${AE_INSTALL}" + ) + target_link_libraries(${TARGET_NAME} PRIVATE c-ares ) +endif() + +if(ESP_PLATFORM) + # aether requires some esp idf components to be requested + # they must exists as cmake targets + list(APPEND idf_components + idf::esp_wifi + idf::esp_netif + idf::nvs_flash + idf::spiffs + idf::esp_driver_uart) + + # test if they exists + foreach(c ${idf_components}) + if (NOT TARGET ${c}) + # component not found, print required components + foreach(cc ${idf_components}) + string(REPLACE "idf::" "" cc_name ${cc}) + set(required_components "${required_components} ${cc_name}") + endforeach() + message(FATAL_ERROR "Component ${c} not found request it by \n\tidf_component_register(REQUIRES ${required_components})") + break() + endif() + endforeach() + + target_link_libraries(${TARGET_NAME} PUBLIC ${idf_components}) +endif() + +if (CMAKE_SYSTEM_NAME MATCHES ".*BSD.*" ) + target_link_libraries(${TARGET_NAME} PRIVATE pthread) +endif() +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + target_compile_definitions(${TARGET_NAME} PUBLIC + NOMINMAX # remove legacy max macro + WIN32_LEAN_AND_MEAN + ) + target_link_libraries(${TARGET_NAME} PRIVATE ws2_32) +endif() + +find_program(GIT_COMMAND git) +if ( GIT_COMMAND ) + # get current git version + execute_process( + COMMAND ${GIT_COMMAND} -C ${CMAKE_CURRENT_SOURCE_DIR} rev-parse --verify HEAD + OUTPUT_VARIABLE GIT_VERSION + ) + if ( NOT GIT_VERSION ) + message(WARNING "Not a git repo") + else() + string(STRIP ${GIT_VERSION} GIT_VERSION) + message(STATUS "get aether git version ${GIT_VERSION}") + endif() +endif() + +if (GIT_VERSION) + target_compile_definitions(${TARGET_NAME} PUBLIC "AE_GIT_VERSION=\"${GIT_VERSION}\"") +endif() +target_compile_definitions(${TARGET_NAME} PUBLIC "AE_PROJECT_VERSION=\"${AE_PROJECT_VERSION}\"") + +target_compile_definitions(${TARGET_NAME} PUBLIC "UTM_ID=${UTM_ID}") + +if (NOT "${USER_CONFIG}" STREQUAL "") + target_compile_definitions(${TARGET_NAME} PUBLIC "USER_CONFIG=\"${USER_CONFIG}\"") +endif() + +if (NOT "${FS_INIT}" STREQUAL "") + target_compile_definitions(${TARGET_NAME} PUBLIC "FS_INIT=\"${FS_INIT}\"") +endif() + +if (AE_DISTILLATION) + target_compile_definitions(${TARGET_NAME} PUBLIC "AE_DISTILLATION=1") +endif() +if (AE_FILTRATION) + target_compile_definitions(${TARGET_NAME} PUBLIC "AE_FILTRATION=1") +endif() + +# for debug purposes only, set registration server ip address +if(NOT "${AE_REG_CLOUD_ADDR}" STREQUAL "") + target_compile_definitions(${TARGET_NAME} PUBLIC "_AE_REG_CLOUD_IP=\"${AE_REG_CLOUD_ADDR}\"") +endif() + +# build with address sanitizer +if (AE_ADDRESS_SANITIZE) + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) +endif() + +# setup warnings an Werror +target_compile_options(${TARGET_NAME} PRIVATE + $<$: + -Werror + -Wall + -Wextra + -Wimplicit-fallthrough + -Wno-missing-braces + -Wno-sign-compare + -Wno-ignored-attributes + -Wno-cpp + # gcc has a bug related to maybe-unitialized related to use std::optional + -Wno-maybe-uninitialized + # gcc often reports false positive, especialy on release builds + -Wno-stringop-overflow + > + $<$: + -Werror + -Wall + -Wextra + -Wconversion + -Wimplicit-fallthrough + -Wno-missing-braces + -Wno-sign-compare + -Wno-ignored-attributes + -Wno-cpp + > + $<$: + /W4 + /WX + /w15262 #implisitfallthrough + /wd4388 #Wno-sign-compare + /wd4389 #Wno-sign-compare + /Zc:preprocessor + > +) + +target_compile_options(${TARGET_NAME} PUBLIC + $<$: + /wd4100 /wd4101 /wd4127 /wd4244 /wd4324 + /wd4456 /wd4459 /wd4714 + > +) + +# setup release build options +if (CMAKE_BUILD_TYPE STREQUAL "Release") + if (NOT MINGW AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU" + OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + + target_compile_options(${TARGET_NAME} PUBLIC + -ffast-math + -fno-math-errno + -funsafe-math-optimizations + -fassociative-math + -freciprocal-math + -ffinite-math-only + -fno-signed-zeros + -fno-trapping-math) + # clang does not support this + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(${TARGET_NAME} PUBLIC + -fsingle-precision-constant + -fno-fp-int-builtin-inexact) + endif() + + # TODO: make this options configurable from user side + target_compile_options(${TARGET_NAME} PRIVATE $<$:-fno-exceptions>) + target_compile_options(${TARGET_NAME} PUBLIC $<$:-fno-rtti>) + + + if (GCC_VERSION VERSION_GREATER 12.1 OR GCC_VERSION VERSION_EQUAL 12.1) + target_compile_options(${TARGET_NAME} PUBLIC -Oz -DNDEBUG) + else() + target_compile_options(${TARGET_NAME} PUBLIC -Os -DNDEBUG) + endif() + + # size optimizations + target_compile_options(${TARGET_NAME} PUBLIC -fdata-sections -ffunction-sections) + target_link_options(${TARGET_NAME} PUBLIC -fdata-sections -ffunction-sections) + target_compile_options(${TARGET_NAME} PUBLIC -flto=auto -ffat-lto-objects) + target_link_options(${TARGET_NAME} PUBLIC -flto=auto) + + if(NOT AE_NO_STRIP_ALL ) + target_link_options(${TARGET_NAME} PUBLIC -Wl,--gc-sections,--strip-all) + endif() + + endif() +endif() + +if(AE_BUILD_TOOLS) + message(STATUS "Aether builds tools!") + add_subdirectory(tools/registrator) +endif() + +if(AE_BUILD_EXAMPLES) + message(STATUS "Aether builds examples!") + + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + add_subdirectory(examples/cloud) + add_subdirectory(examples/capi/oddity) + add_subdirectory(examples/benches/send_message_delays) + add_subdirectory(examples/benches/send_messages_bandwidth) +endif() + +if(AE_BUILD_TESTS) + message(STATUS "Aether builds tests!") + enable_testing() + + target_link_libraries(${TARGET_NAME} PRIVATE inline_tests) + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/tests ${CMAKE_BINARY_DIR}/tests) +endif() + +if(AE_INSTALL) + include(CMakePackageConfigHelpers) + include(GNUInstallDirs) + + # Target installation + install(TARGETS ${TARGET_NAME} + EXPORT ${TARGET_NAME}Targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${TARGET_NAME}) + + install(DIRECTORY ${TARGET_NAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ FILES_MATCHING PATTERN "*.h") + + # Target's cmake files: targets export + install(EXPORT ${TARGET_NAME}Targets + NAMESPACE ${TARGET_NAME}:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET_NAME}) +endif() diff --git a/aether/CMakeLists.txt b/aether/CMakeLists.txt index baa251df..12f5d9b0 100644 --- a/aether/CMakeLists.txt +++ b/aether/CMakeLists.txt @@ -14,38 +14,6 @@ cmake_minimum_required(VERSION 3.16.0) -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -set(AE_PROJECT_VERSION "0.1.0") - -option(AE_NO_STRIP_ALL "Do not apply --strip_all, useful for bloaty and similar tools " Off) -option(AE_DISTILLATION "Build aether in distillation mode" On) -option(AE_FILTRATION "Build aether in filtration mode" Off) -option(AE_BUILD_TESTS "Build tests" Off) - -set(UTM_ID "0" CACHE STRING "User Tracking Measurement ID, must be a uint32 value") -set(USER_CONFIG "" CACHE PATH "Path to user provided configuration header file") -set(FS_INIT "" CACHE PATH "Path to user provided saved state header file") - -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../cmake) -include(repo_init) - -ae_update_dependencies() - -# list a common dependencies -list(APPEND common_dependencies - "../third_party/libbcrypt" - "../third_party/libhydrogen" - "../third_party/libsodium" - "../third_party/gcem" - "../third_party/etl" - "../third_party/aethernet-numeric" -) - -# for etl -set(GIT_DIR_LOOKUP_POLICY ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR) - list(APPEND aether_srcs "aether_app.cpp" "aether.cpp" @@ -58,32 +26,31 @@ list(APPEND aether_srcs "server_keys.cpp" "socket_initializer.cpp") -list(APPEND aether_c_api_srcs +list(APPEND aether_srcs "aether_c/c_uid.cpp" - "aether_c/aether_capi.cpp" -) + "aether_c/aether_capi.cpp") -list(APPEND types_srcs +list(APPEND aether_srcs "types/address.cpp") -list(APPEND events_srcs +list(APPEND aether_srcs "events/event_list.cpp" "events/event_deleter.cpp" "events/event_subscription.cpp" "events/multi_subscription.cpp") -list(APPEND poller_srcs +list(APPEND aether_srcs "poller/epoll_poller.cpp" "poller/kqueue_poller.cpp" "poller/freertos_poller.cpp" "poller/win_poller.cpp") -list(APPEND dns_srcs +list(APPEND aether_srcs "dns/dns_resolve.cpp" "dns/dns_c_ares.cpp" "dns/esp32_dns_resolve.cpp") -list(APPEND obj_srcs +list(APPEND aether_srcs "obj/obj.cpp" "obj/domain.cpp" "obj/obj_id.cpp" @@ -91,13 +58,13 @@ list(APPEND obj_srcs "obj/obj_ptr_base.cpp" ) -list(APPEND ptr_srcs +list(APPEND aether_srcs "ptr/ptr.cpp" "ptr/ptr_view.cpp" "ptr/ref_tree.cpp" ) -list(APPEND actions_srcs +list(APPEND aether_srcs "ae_actions/get_client_cloud.cpp" "ae_actions/get_servers.cpp" "ae_actions/ping.cpp" @@ -105,15 +72,9 @@ list(APPEND actions_srcs "ae_actions/telemetry.cpp" "ae_actions/select_client.cpp" "ae_actions/time_sync.cpp" - "actions/action_trigger.cpp" - "actions/action_registry.cpp" - "actions/action_processor.cpp" - "actions/timer_action.cpp" - "actions/task_queue.cpp" - "actions/repeatable_task.cpp" ) -list(APPEND registration_srcs +list(APPEND aether_srcs "registration/api/client_reg_api_safe.cpp" "registration/api/client_reg_api_unsafe.cpp" "registration/api/global_reg_client_api.cpp" @@ -125,11 +86,11 @@ list(APPEND registration_srcs "registration/registration_crypto_provider.cpp" "registration/root_server_select_stream.cpp") -list(APPEND uap_srcs +list(APPEND aether_srcs "uap/uap.cpp" ) -list(APPEND adapters_srcs +list(APPEND aether_srcs "adapters/adapter.cpp" "adapters/wifi_adapter.cpp" "adapters/ethernet.cpp" @@ -140,36 +101,35 @@ list(APPEND adapters_srcs "adapters/parent_wifi.cpp" ) -list(APPEND access_points_srcs +list(APPEND aether_srcs "access_points/access_point.cpp" "access_points/ethernet_access_point.cpp" "access_points/wifi_access_point.cpp" "access_points/lora_module_access_point.cpp" "access_points/modem_access_point.cpp") -list(APPEND modems_srcs +list(APPEND aether_srcs "modems/modem_factory.cpp" "modems/bg95_at_modem.cpp" "modems/sim7070_at_modem.cpp" "modems/thingy91x_at_modem.cpp") -list(APPEND lora_modules_srcs +list(APPEND aether_srcs "lora_modules/dx_smart_lr02_lm.cpp" "lora_modules/ebyte_e22_lm.cpp" "lora_modules/lora_module_factory.cpp") -list(APPEND gateway_api_srcs +list(APPEND aether_srcs "gateway_api/gateway_api.cpp") -list(APPEND wifi_srcs +list(APPEND aether_srcs "wifi/wifi_driver_factory.cpp" "wifi/esp_wifi_driver.cpp" ) -list(APPEND serial_ports_srcs +list(APPEND aether_srcs "serial_ports/at_support/at_buffer.cpp" "serial_ports/at_support/at_dispatcher.cpp" - "serial_ports/at_support/at_request.cpp" "serial_ports/at_support/at_listener.cpp" "serial_ports/at_support/at_support.cpp" "serial_ports/serial_port_factory.cpp" @@ -178,7 +138,7 @@ list(APPEND serial_ports_srcs "serial_ports/unix_serial_port.cpp" ) -list(APPEND transport_srcs +list(APPEND aether_srcs "transport/system_sockets/sockets/get_sock_addr.cpp" "transport/system_sockets/sockets/lwip_cb_tcp_socket.cpp" "transport/system_sockets/sockets/lwip_cb_udp_socket.cpp" @@ -186,8 +146,6 @@ list(APPEND transport_srcs "transport/system_sockets/sockets/lwip_socket.cpp" "transport/system_sockets/sockets/lwip_tcp_socket.cpp" "transport/system_sockets/sockets/lwip_udp_socket.cpp" - "transport/system_sockets/sockets/tcp_sockets_factory.cpp" - "transport/system_sockets/sockets/udp_sockets_factory.cpp" "transport/system_sockets/sockets/unix_socket.cpp" "transport/system_sockets/sockets/unix_tcp_socket.cpp" "transport/system_sockets/sockets/unix_udp_socket.cpp" @@ -195,7 +153,6 @@ list(APPEND transport_srcs "transport/system_sockets/sockets/win_tcp_socket.cpp" "transport/system_sockets/sockets/win_udp_socket.cpp" "transport/modems/modem_transport.cpp" - "transport/socket_packet_send_action.cpp" "transport/data_packet_collector.cpp" "transport/system_sockets/tcp/tcp.cpp" "transport/system_sockets/udp/udp.cpp" @@ -203,35 +160,23 @@ list(APPEND transport_srcs "transport/gateway/gateway_transport.cpp" ) -list(APPEND stream_api_src +list(APPEND aether_srcs "stream_api/sized_packet_gate.cpp" "stream_api/stream_api.cpp" - - "stream_api/safe_stream.cpp" - "stream_api/safe_stream/safe_stream_api.cpp" - "stream_api/safe_stream/sending_data_action.cpp" - "stream_api/safe_stream/send_data_buffer.cpp" - "stream_api/safe_stream/sending_chunk_list.cpp" - "stream_api/safe_stream/receiving_chunk_list.cpp" - "stream_api/safe_stream/safe_stream_send_action.cpp" - "stream_api/safe_stream/safe_stream_recv_action.cpp" ) -list(APPEND write_action_srcs - "write_action/write_action.cpp" - "write_action/failed_write_action.cpp" - "write_action/done_write_action.cpp" +list(APPEND aether_srcs "write_action/buffer_write.cpp" ) -list(APPEND api_protocol_srcs +list(APPEND aether_srcs "api_protocol/protocol_context.cpp" "api_protocol/api_pack_parser.cpp" "api_protocol/child_data.cpp" "api_protocol/return_result_api.cpp" ) -list(APPEND crypto_srcs +list(APPEND aether_srcs "crypto/crypto_nonce.cpp" "crypto/signed_key.cpp" "crypto/key_gen.cpp" @@ -243,35 +188,35 @@ list(APPEND crypto_srcs "crypto/hydrogen/hydro_sync_crypto_provider.cpp" ) -list(APPEND work_cloud_api_srcs +list(APPEND aether_srcs "work_cloud_api/work_server_api/login_api.cpp" "work_cloud_api/work_server_api/authorized_api.cpp" "work_cloud_api/client_api/client_api_unsafe.cpp" "work_cloud_api/client_api/client_api_safe.cpp") -list(APPEND cloud_connections_srcs +list(APPEND aether_srcs "cloud_connections/cloud_server_connection.cpp" "cloud_connections/cloud_server_connections.cpp" "cloud_connections/cloud_subscription.cpp" "cloud_connections/cloud_request.cpp") -list(APPEND server_connections_srcs +list(APPEND aether_srcs "server_connections/client_server_connection.cpp" "server_connections/channel_connection.cpp" "server_connections/server_connection.cpp") -list(APPEND connection_manager_srcs +list(APPEND aether_srcs "connection_manager/client_connection_manager.cpp" "connection_manager/client_cloud_manager.cpp" "connection_manager/server_connection_manager.cpp") -list(APPEND client_messages_srcs +list(APPEND aether_srcs "client_messages/p2p_message_stream.cpp" "client_messages/p2p_message_stream_manager.cpp" "client_messages/p2p_safe_message_stream.cpp") -list(APPEND domain_storage_srcs +list(APPEND aether_srcs "domain_storage/domain_storage_factory.cpp" "domain_storage/registrar_domain_storage.cpp" "domain_storage/static_domain_storage.cpp" @@ -280,7 +225,7 @@ list(APPEND domain_storage_srcs "domain_storage/file_system_std_storage.cpp" "domain_storage/sync_domain_storage.cpp") -list(APPEND channels_srcs +list(APPEND aether_srcs "channels/channel_statistics.cpp" "channels/channel.cpp" "channels/ethernet_transport_factory.cpp" @@ -289,285 +234,10 @@ list(APPEND channels_srcs "channels/lora_module_channel.cpp" "channels/modem_channel.cpp") -list(APPEND tele_srcs +list(APPEND aether_srcs "tele/tele_init.cpp" "tele/traps/io_stream_traps.cpp" "tele/traps/statistics_trap.cpp" "tele/traps/tele_statistics.cpp") -message(STATUS "Cmake system name is ${CMAKE_SYSTEM_NAME}") - -if(NOT CM_PLATFORM) - message(STATUS "Aether build for regular cmake project") - set(REGULAR_CMAKE_PROJECT On) -else() - message(STATUS "Aether build for CM_PLATFORM=${CM_PLATFORM}") -endif() - -if(REGULAR_CMAKE_PROJECT) - project(aether VERSION ${AE_PROJECT_VERSION} LANGUAGES CXX C) - - set(CARES_BUILD_TOOLS OFF CACHE BOOL "" FORCE) - set(CARES_STATIC ON CACHE BOOL "" FORCE) - set(CARES_SHARED OFF CACHE BOOL "" FORCE) - list(APPEND common_dependencies "../third_party/c-ares") - - foreach(dep ${common_dependencies}) - get_filename_component(DEP_NAME ${dep} NAME) - add_subdirectory(${dep} "${CMAKE_CURRENT_BINARY_DIR}/${DEP_NAME}") - endforeach() - - add_library(aether STATIC ${aether_srcs} - ${aether_c_api_srcs} - ${types_srcs} - ${events_srcs} - ${poller_srcs} - ${dns_srcs} - ${obj_srcs} - ${ptr_srcs} - ${actions_srcs} - ${transport_srcs} - ${stream_api_src} - ${write_action_srcs} - ${uap_srcs} - ${adapters_srcs} - ${access_points_srcs} - ${modems_srcs} - ${lora_modules_srcs} - ${gateway_api_srcs} - ${wifi_srcs} - ${serial_ports_srcs} - ${api_protocol_srcs} - ${crypto_srcs} - ${work_cloud_api_srcs} - ${registration_srcs} - ${client_messages_srcs} - ${cloud_connections_srcs} - ${server_connections_srcs} - ${connection_manager_srcs} - ${domain_storage_srcs} - ${channels_srcs} - ${tele_srcs} - ) - - # https://stackoverflow.com/questions/25676277/cmake-target-include-directories-prints-an-error-when-i-try-to-add-the-source - target_include_directories(${PROJECT_NAME} PUBLIC - $ - $ - ) - target_link_libraries(${PROJECT_NAME} PUBLIC - bcrypt - sodium - hydrogen - gcem - etl) - target_link_libraries(${PROJECT_NAME} PRIVATE c-ares ) - - set(TARGET_NAME "${PROJECT_NAME}") -else() - idf_build_get_property(CM_PLATFORM CM_PLATFORM) - if(CM_PLATFORM STREQUAL "ESP32") - #ESP32 CMake - set(include_dirs "../") - - idf_component_register( SRCS - ${aether_srcs} - ${aether_c_api_srcs} - ${types_srcs} - ${events_srcs} - ${poller_srcs} - ${dns_srcs} - ${obj_srcs} - ${ptr_srcs} - ${actions_srcs} - ${uap_srcs} - ${adapters_srcs} - ${access_points_srcs} - ${modems_srcs} - ${lora_modules_srcs} - ${gateway_api_srcs} - ${wifi_srcs} - ${serial_ports_srcs} - ${transport_srcs} - ${stream_api_src} - ${write_action_srcs} - ${api_protocol_srcs} - ${crypto_srcs} - ${work_cloud_api_srcs} - ${registration_srcs} - ${client_messages_srcs} - ${cloud_connections_srcs} - ${server_connections_srcs} - ${connection_manager_srcs} - ${domain_storage_srcs} - ${channels_srcs} - ${tele_srcs} - INCLUDE_DIRS ${include_dirs} - REQUIRES esp_wifi nvs_flash spiffs esp_driver_uart) - - set(TARGET_NAME "${COMPONENT_LIB}") - - foreach(dep ${common_dependencies}) - get_filename_component(DEP_NAME ${dep} NAME) - add_subdirectory(${dep} ${DEP_NAME}) - endforeach() - - target_link_libraries(${TARGET_NAME} PUBLIC - bcrypt - sodium - hydrogen - gcem - etl) - else() - #ERROR - message(SEND_ERROR "You must specify the CMAKE version!") - endif() -endif() - -if (CMAKE_SYSTEM_NAME MATCHES ".*BSD.*" ) - target_link_libraries(${TARGET_NAME} PRIVATE pthread) -endif() -if (CMAKE_SYSTEM_NAME STREQUAL "Windows") - target_compile_definitions(${TARGET_NAME} PUBLIC - NOMINMAX # remove legacy max macro - WIN32_LEAN_AND_MEAN - ) - target_link_libraries(${TARGET_NAME} PRIVATE ws2_32) -endif() - -find_program(GIT_COMMAND git) -if ( GIT_COMMAND ) - # get current git version - execute_process( - COMMAND ${GIT_COMMAND} -C ${CMAKE_CURRENT_SOURCE_DIR} rev-parse --verify HEAD - OUTPUT_VARIABLE GIT_VERSION - ) - if ( NOT GIT_VERSION ) - message(WARNING "Not a git repo") - else() - string(STRIP ${GIT_VERSION} GIT_VERSION) - message(STATUS "get aether git version ${GIT_VERSION}") - endif() -endif() - -if (GIT_VERSION) - target_compile_definitions(${TARGET_NAME} PUBLIC "AE_GIT_VERSION=\"${GIT_VERSION}\"") -endif() -target_compile_definitions(${TARGET_NAME} PUBLIC "AE_PROJECT_VERSION=\"${AE_PROJECT_VERSION}\"") - -target_compile_definitions(${TARGET_NAME} PUBLIC "UTM_ID=${UTM_ID}") - -if (NOT "${USER_CONFIG}" STREQUAL "") - target_compile_definitions(${TARGET_NAME} PUBLIC "USER_CONFIG=\"${USER_CONFIG}\"") -endif() - -if (NOT "${FS_INIT}" STREQUAL "") - target_compile_definitions(${TARGET_NAME} PUBLIC "FS_INIT=\"${FS_INIT}\"") -endif() - -if (AE_DISTILLATION) - target_compile_definitions(${TARGET_NAME} PUBLIC "AE_DISTILLATION=1") -endif() -if (AE_FILTRATION) - target_compile_definitions(${TARGET_NAME} PUBLIC "AE_FILTRATION=1") -endif() - -# for debug purposes only, set registration server ip address -if(NOT "${_AE_REG_CLOUD_IP}" STREQUAL "") - target_compile_definitions(${TARGET_NAME} PUBLIC "_AE_REG_CLOUD_IP=\"${_AE_REG_CLOUD_IP}\"") -endif() - -# match GCC abd either Clang or AppleClang -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*") - target_compile_options(${TARGET_NAME} PRIVATE - -Werror - -Wall - -Wextra - -Wimplicit-fallthrough - -Wno-missing-braces - -Wno-sign-compare - -Wno-ignored-attributes - -Wno-cpp) - # gcc before 10.0 has bagous Wconversion check - if (GCC_VERSION VERSION_GREATER 10.0 OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*") - target_compile_options(${TARGET_NAME} PRIVATE -Wconversion) - endif() - - if (CMAKE_BUILD_TYPE STREQUAL "Release") - target_compile_options(${TARGET_NAME} PUBLIC - -ffast-math - -fno-math-errno - -funsafe-math-optimizations - -fassociative-math - -freciprocal-math - -ffinite-math-only - -fno-signed-zeros - -fno-trapping-math) - - # clang does not support this - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(${TARGET_NAME} PUBLIC - -fsingle-precision-constant - -fno-fp-int-builtin-inexact) - endif() - - if (GCC_VERSION VERSION_GREATER 12.1 OR GCC_VERSION VERSION_EQUAL 12.1) - target_compile_options(${TARGET_NAME} PUBLIC -Oz -DNDEBUG) - else() - target_compile_options(${TARGET_NAME} PUBLIC -Os -DNDEBUG) - endif() - - target_compile_options(${TARGET_NAME} PRIVATE $<$:-fno-exceptions>) - target_compile_options(${TARGET_NAME} PUBLIC $<$:-fno-rtti>) - - # macos's ld does not support --gc-sections,--strip-all - if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") - target_compile_options(${TARGET_NAME} PUBLIC -fdata-sections -ffunction-sections) - target_link_options(${TARGET_NAME} PUBLIC -fdata-sections -ffunction-sections) - if(NOT AE_NO_STRIP_ALL ) - target_link_options(${TARGET_NAME} PUBLIC -Wl,--gc-sections,--strip-all) - endif() - if (NOT MINGW) - target_compile_options(${TARGET_NAME} PUBLIC -flto=auto -ffat-lto-objects) - target_link_options(${TARGET_NAME} PUBLIC -flto=auto) - endif() - endif() - endif() - -elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - target_compile_options(${TARGET_NAME} - PRIVATE - /W4 - /WX - /wd4388 #Wno-sign-compare - /wd4389 #Wno-sign-compare - /wd4244 - /w15262 #implisitfallthrough - ) - target_compile_options(${TARGET_NAME} PUBLIC /Zc:preprocessor) -endif() - -if(AE_BUILD_TESTS) - message(STATUS "Aether builds tests!") - # enable doubles in unity tests - add_compile_definitions("UNITY_INCLUDE_DOUBLE") - - target_link_libraries(${TARGET_NAME} PRIVATE inline_tests) - add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../tests ${CMAKE_BINARY_DIR}/tests) -endif() - - -if(REGULAR_CMAKE_PROJECT AND NOT AE_BUILD_TESTS) - ## Target installation - install(TARGETS ${TARGET_NAME} - EXPORT ${TARGET_NAME}Targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${TARGET_NAME} - COMPONENT library) - - ## Target's cmake files: targets export - install(EXPORT ${TARGET_NAME}Targets - NAMESPACE ${TARGET_NAME}:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET_NAME}) -endif() +target_sources(${TARGET_NAME} PRIVATE ${aether_srcs}) diff --git a/aether/access_points/modem_access_point.cpp b/aether/access_points/modem_access_point.cpp index 19830ca9..6cb1dd48 100644 --- a/aether/access_points/modem_access_point.cpp +++ b/aether/access_points/modem_access_point.cpp @@ -24,47 +24,53 @@ # include "aether/channels/modem_channel.h" # include "aether/access_points/filter_endpoints.h" +# include "aether/tele/tele.h" + namespace ae { -ModemConnectAction::ModemConnectAction(ActionContext action_context, - [[maybe_unused]] IModemDriver& driver) - : Action{action_context}, driver_{&driver}, state_{State::kStart} { +ModemConnectAction::ModemConnectAction(AeContext const& ae_context, + IModemDriver& driver) + : driver_{&driver}, + task_sub_{ae_context.scheduler().Task([&]() noexcept { Start(); })} { AE_TELED_DEBUG("ModemConnectAction created"); } -UpdateStatus ModemConnectAction::Update() { - if (state_.changed()) { - switch (state_.Acquire()) { - case State::kStart: - Start(); - break; - case State::kSuccess: - return UpdateStatus::Result(); - case State::kFailed: - return UpdateStatus::Error(); - } - } - return {}; +ModemConnectAction::ConnectionEvent::Subscriber +ModemConnectAction::connection_event() { + return EventSubscriber{connection_event_}; } void ModemConnectAction::Start() { AE_TELED_DEBUG("ModemConnectAction start"); - auto action = driver_->Start(); - if (!action) { - state_ = State::kFailed; - Action::Trigger(); + auto* op = driver_->Start(); + if (op == nullptr) { + connection_event_.Emit(false); + Finish(); + return; + } + // check immediate result + if (auto const& res = op->result(); res) { + if (res->IsOk()) { + AE_TELED_INFO("Modem access point start success"); + connection_event_.Emit(true); + } else { + AE_TELED_ERROR("Modem access point start failed, error {}", + static_cast(res->error())); + connection_event_.Emit(false); + } + Finish(); return; } - action->StatusEvent().Subscribe(ActionHandler{ - OnResult{[this]() { - AE_TELED_INFO("Modem access point start success"); - state_ = State::kSuccess; - Action::Trigger(); - }}, - OnError{[this]() { - AE_TELED_ERROR("Modem access point start failed"); - state_ = State::kFailed; - Action::Trigger(); - }}, + + start_sub_ = op->result_event().Subscribe([this](auto const& res) { + if (res) { + AE_TELED_INFO("Modem access point start success"); + connection_event_.Emit(true); + } else { + AE_TELED_ERROR("Modem access point start failed, error {}", + static_cast(res.error())); + connection_event_.Emit(false); + } + Finish(); }); } @@ -74,17 +80,15 @@ ModemAccessPoint::ModemAccessPoint(ObjProp prop, ObjPtr aether, aether_{std::move(aether)}, modem_adapter_{std::move(modem_adapter)} {} -ActionPtr ModemAccessPoint::Connect() { +ModemConnectAction& ModemAccessPoint::Connect() { AE_TELED_DEBUG("Make modem access point connection"); // reuse connect action if it's in progress - if (!connect_action_) { - connect_action_ = ActionPtr{ - *aether_.Load().as(), modem_adapter_->modem_driver()}; - connect_sub_ = connect_action_->FinishedEvent().Subscribe( - [this]() { connect_action_.reset(); }); + if (!connect_action_ || connect_action_->is_finished()) { + connect_action_.emplace(*aether_.Load().as(), + modem_adapter_->modem_driver()); } - return connect_action_; + return *connect_action_; } IModemDriver& ModemAccessPoint::modem_driver() { diff --git a/aether/access_points/modem_access_point.h b/aether/access_points/modem_access_point.h index f2ba99ea..0c5672d3 100644 --- a/aether/access_points/modem_access_point.h +++ b/aether/access_points/modem_access_point.h @@ -21,10 +21,9 @@ #if AE_SUPPORT_MODEMS # include "aether/obj/obj.h" +# include "aether/ae_context.h" +# include "aether/events/events.h" # include "aether/actions/action.h" -# include "aether/actions/action_ptr.h" -# include "aether/types/state_machine.h" -# include "aether/modems/imodem_driver.h" # include "aether/adapters/modem_adapter.h" # include "aether/events/event_subscription.h" # include "aether/access_points/access_point.h" @@ -32,24 +31,22 @@ namespace ae { class Aether; -class ModemConnectAction final : public Action { +class ModemConnectAction final : public Action { public: - enum class State : std::uint8_t { - kStart, - kSuccess, - kFailed, - }; + using ConnectionEvent = Event; - ModemConnectAction(ActionContext action_context, IModemDriver& driver); + explicit ModemConnectAction(AeContext const& ae_context, + IModemDriver& driver); - UpdateStatus Update(); + ConnectionEvent::Subscriber connection_event(); private: void Start(); IModemDriver* driver_; + ConnectionEvent connection_event_; Subscription start_sub_; - StateMachine state_; + TaskSubscription task_sub_; }; class ModemAccessPoint final : public AccessPoint { @@ -62,7 +59,7 @@ class ModemAccessPoint final : public AccessPoint { AE_OBJECT_REFLECT(AE_MMBRS(aether_, modem_adapter_)); - ActionPtr Connect(); + ModemConnectAction& Connect(); IModemDriver& modem_driver(); std::vector> GenerateChannels( @@ -71,7 +68,7 @@ class ModemAccessPoint final : public AccessPoint { private: Obj::ptr aether_; ModemAdapter::ptr modem_adapter_; - ActionPtr connect_action_; + std::optional connect_action_; Subscription connect_sub_; }; } // namespace ae diff --git a/aether/access_points/wifi_access_point.cpp b/aether/access_points/wifi_access_point.cpp index dc3f9cd9..21a81c4c 100644 --- a/aether/access_points/wifi_access_point.cpp +++ b/aether/access_points/wifi_access_point.cpp @@ -29,47 +29,35 @@ namespace ae { -WifiConnectAction::WifiConnectAction(ActionContext action_context, +WifiConnectAction::WifiConnectAction(AeContext const& ae_context, WifiDriver& driver, WiFiAp wifi_ap, WiFiPowerSaveParam psp, WiFiBaseStation& base_station) - : Action{action_context}, + : ae_context_{ae_context}, driver_{&driver}, wifi_ap_{std::move(wifi_ap)}, psp_{std::move(psp)}, base_station_{base_station}, - state_{State::kCheckIsConnected} {} + scheduler_sub_{ + ae_context_.scheduler().Task([this]() { EnsureConnected(); })} {} -UpdateStatus WifiConnectAction::Update() { - if (state_.changed()) { - switch (state_.Acquire()) { - case State::kCheckIsConnected: { - auto connected_to = driver_->connected_to(); - if (connected_to.ssid == wifi_ap_.creds.ssid) { - state_ = State::kConnected; - } else { - state_ = State::kConnect; - } - Action::Trigger(); - break; - } - case State::kConnect: { - driver_->Connect(wifi_ap_, psp_, base_station_); - // TODO: Does connect should be async - state_ = State::kConnected; - Action::Trigger(); - break; - } - case State::kConnected: - return UpdateStatus::Result(); - case State::kFailed: - return UpdateStatus::Error(); - } +WifiConnectAction::ConnectionEvent::Subscriber +WifiConnectAction::connection_event() { + return EventSubscriber{connection_event_}; +} + +void WifiConnectAction::EnsureConnected() { + auto connected_to = driver_->connected_to(); + if (connected_to.ssid != wifi_ap_.creds.ssid) { + driver_->Connect(wifi_ap_, psp_, base_station_); } - return {}; + SetConnected(true); } -WifiConnectAction::State WifiConnectAction::state() const { return state_; } +void WifiConnectAction::SetConnected(bool is_connected) { + connection_event_.Emit(is_connected); + Finish(); +} WifiAccessPoint::WifiAccessPoint() = default; @@ -110,19 +98,17 @@ std::vector> WifiAccessPoint::GenerateChannels( return channels; } -ActionPtr WifiAccessPoint::Connect() { +WifiConnectAction& WifiAccessPoint::Connect() { // reuse connect action if it's in progress - if (!connect_action_) { + if (!connect_action_ || connect_action_->is_finished()) { auto driver = WifiAdapter::ptr{adapter_}.WithLoaded( [](auto const& a) { return &a->driver(); }); assert(driver.has_value()); - connect_action_ = ActionPtr{ - *aether_.Load().as(), **driver, wifi_ap_, psp_, base_station_}; - connect_sub_ = connect_action_->FinishedEvent().Subscribe( - [this]() { connect_action_.reset(); }); + connect_action_.emplace(*aether_.Load().as(), **driver, wifi_ap_, + psp_, base_station_); } - return connect_action_; + return *connect_action_; } bool WifiAccessPoint::IsConnected() { @@ -134,6 +120,5 @@ bool WifiAccessPoint::IsConnected() { }) .value_or(false); } - } // namespace ae #endif diff --git a/aether/access_points/wifi_access_point.h b/aether/access_points/wifi_access_point.h index dd1d5d9d..79ff4112 100644 --- a/aether/access_points/wifi_access_point.h +++ b/aether/access_points/wifi_access_point.h @@ -17,16 +17,15 @@ #ifndef AETHER_ACCESS_POINTS_WIFI_ACCESS_POINT_H_ #define AETHER_ACCESS_POINTS_WIFI_ACCESS_POINT_H_ -#include +#include #include "aether/config.h" #if AE_SUPPORT_WIFIS +# include "aether/ae_context.h" # include "aether/obj/obj_ptr.h" +# include "aether/events/events.h" # include "aether/actions/action.h" -# include "aether/actions/action_ptr.h" -# include "aether/types/state_machine.h" -# include "aether/actions/action_context.h" # include "aether/wifi/wifi_driver.h" # include "aether/access_points/access_point.h" @@ -37,29 +36,27 @@ class WifiAdapter; class IPoller; class DnsResolver; -class WifiConnectAction final : public Action { +class WifiConnectAction final : public Action { public: - enum class State : std::uint8_t { - kCheckIsConnected, - kConnect, - kConnected, - kFailed, - }; - - WifiConnectAction(ActionContext action_context, WifiDriver& driver, + using ConnectionEvent = Event; + + WifiConnectAction(AeContext const& ae_context, WifiDriver& driver, WiFiAp wifi_ap, WiFiPowerSaveParam psp, WiFiBaseStation& base_station); - UpdateStatus Update(); - - State state() const; + ConnectionEvent::Subscriber connection_event(); private: + void EnsureConnected(); + void SetConnected(bool is_connected); + + AeContext ae_context_; WifiDriver* driver_; WiFiAp wifi_ap_; WiFiPowerSaveParam psp_; WiFiBaseStation& base_station_; - StateMachine state_; + ConnectionEvent connection_event_; + TaskSubscription scheduler_sub_; }; class WifiAccessPoint final : public AccessPoint { @@ -81,7 +78,7 @@ class WifiAccessPoint final : public AccessPoint { /** * \brief Connect or ensure it's connected to current access point. */ - ActionPtr Connect(); + WifiConnectAction& Connect(); bool IsConnected(); @@ -93,8 +90,7 @@ class WifiAccessPoint final : public AccessPoint { WiFiAp wifi_ap_{}; WiFiPowerSaveParam psp_{}; WiFiBaseStation base_station_{}; - ActionPtr connect_action_; - Subscription connect_sub_; + std::optional connect_action_; }; } // namespace ae #endif diff --git a/aether/actions/action.h b/aether/actions/action.h index 88b0de7b..c7710040 100644 --- a/aether/actions/action.h +++ b/aether/actions/action.h @@ -1,5 +1,5 @@ /* - * Copyright 2024 Aethernet Inc. + * 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. @@ -17,138 +17,33 @@ #ifndef AETHER_ACTIONS_ACTION_H_ #define AETHER_ACTIONS_ACTION_H_ -#include "aether/clock.h" - -// IWYU pragma: begin_exports +#include "aether/common.h" #include "aether/events/events.h" -#include "aether/actions/update_status.h" -#include "aether/actions/action_context.h" -#include "aether/actions/action_trigger.h" -#include "aether/actions/action_event_status.h" -// IWYU pragma: end_exports namespace ae { -namespace action_internal { -template -struct HasUpdate : std::false_type {}; - -template -struct HasUpdate> : std::true_type {}; -} // namespace action_internal - -// Base Action class -class IAction { +class Action { public: - explicit IAction(ActionTrigger* action_trigger) - : action_trigger_{action_trigger} {} + using FinishedEvent = Event; - virtual ~IAction() = default; + Action() noexcept = default; + virtual ~Action() noexcept = default; - AE_CLASS_MOVE_ONLY(IAction); + AE_CLASS_MOVE_ONLY(Action) - virtual TimePoint ActionUpdate(TimePoint current_time) = 0; - - bool IsFinished() const { return finished_; } - - [[nodiscard]] auto FinishedEvent() { - return EventSubscriber{finished_event_}; - } - - // Trigger an event on action which leads to run action's Update as soon as - // possible. - void Trigger() { - if (action_trigger_ != nullptr) { - action_trigger_->Trigger(); - } - } - - // Action is finished all it's job and may be removed. - void Finish() { - Trigger(); - finished_ = true; + void Finish() noexcept { + is_finished_ = true; finished_event_.Emit(); } - protected: - Event finished_event_; - - ActionTrigger* action_trigger_{}; - bool finished_{false}; -}; - -/** - * \brief Common action done/reject interface. - * Inherit from this class to implement your own action. - * Call Result() in case of success, Error in case of failure and Stop() in case - * of rejection. - */ -template -class Action : public IAction { - public: - explicit Action(ActionContext action_context) - : IAction{&action_context.get_trigger()} { - Trigger(); - } - - AE_CLASS_MOVE_ONLY(Action); - - /** - * \brief Action Update logic. - * Each T should provide an UpdateStatus Update([time_point]) method. - * optional time point will be set to current_time. - * UpdateStatus represent current Update result and may be one of: - * kNothing, kResult, kError, kStop, kDelay - * kNothing - indicates action currently has status update - * kDelay - indicates action want to cal next Update at least before returned - * time point. kResult, kError, kStop - for result, error and stop event and - * leads to call Status method and StatusEvent. - */ - TimePoint ActionUpdate(TimePoint current_time) override { - if constexpr (!action_internal::HasUpdate::value) { - return current_time; - } else { - auto& self = *static_cast(this); - UpdateStatus res; - if constexpr (std::is_invocable_v) { - res = self.Update(current_time); - } else { - res = self.Update(); - } - switch (res.type) { - case UpdateStatusType::kNothing: { - break; - } - case UpdateStatusType::kResult: - case UpdateStatusType::kError: - case UpdateStatusType::kStop: { - Status(self, res); - break; - } - case UpdateStatusType::kDelay: { - return res.delay_to; - } - } - return current_time; - } + FinishedEvent::Subscriber finished_event() noexcept { + return EventSubscriber{finished_event_}; } - /** - * Add callback to be called when action is done result is encoded as - * ActionEventResult. - */ - [[nodiscard]] auto StatusEvent() { return EventSubscriber{action_result_}; } - - using IAction::Trigger; - - protected: - // Set action status and emit status event, and make action finished. - void Status(T& object, UpdateStatus const& result) { - action_result_.Emit(ActionEventStatus{object, result}); - Finish(); - } + bool is_finished() const noexcept { return is_finished_; } private: - Event)> action_result_; + bool is_finished_ = false; + FinishedEvent finished_event_; }; } // namespace ae diff --git a/aether/actions/action_context.h b/aether/actions/action_context.h index 359fa29a..b3f79612 100644 --- a/aether/actions/action_context.h +++ b/aether/actions/action_context.h @@ -17,34 +17,23 @@ #ifndef AETHER_ACTIONS_ACTION_CONTEXT_H_ #define AETHER_ACTIONS_ACTION_CONTEXT_H_ -#include "aether/actions/action_registry.h" -#include "aether/actions/action_trigger.h" +#include -namespace ae { -namespace action_internal { -template -struct ActionContextConcept : std::false_type {}; +#include "aether/tasks/details/task_subsctiption.h" +namespace ae { template -struct ActionContextConcept< - T, std::void_t().get_trigger()), - decltype(std::declval().get_registry())>> - : std::true_type {}; -} // namespace action_internal - -class ActionContext { - public: - template ), - AE_REQUIRERS_NOT((std::is_same>))> - ActionContext(T&& context) - : trigger_{&context.get_trigger()}, registry_{&context.get_registry()} {} - - ActionTrigger& get_trigger() { return *trigger_; } - ActionRegistry& get_registry() { return *registry_; } +concept AsyncScheduler = requires(T& t) { + TaskSubscription{t.Task([]() {})}; + TaskSubscription{ + t.DelayedTask([]() {}, std::declval())}; + TaskSubscription{t.DelayedTask( + []() {}, std::declval())}; +}; - private: - ActionTrigger* trigger_; - ActionRegistry* registry_; +template +concept ActionContext = requires(T t) { + { t.scheduler() } -> AsyncScheduler; }; } // namespace ae diff --git a/aether/actions/action_event_status.h b/aether/actions/action_event_status.h deleted file mode 100644 index ee74f9c5..00000000 --- a/aether/actions/action_event_status.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2025 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 AETHER_ACTIONS_ACTION_EVENT_STATUS_H_ -#define AETHER_ACTIONS_ACTION_EVENT_STATUS_H_ - -#include -#include -#include -#include - -#include "aether/common.h" -#include "aether/actions/update_status.h" - -namespace ae { -/** - * \brief The result action returns in it's event. - */ -template -class ActionEventStatus { - public: - ActionEventStatus() = default; - - constexpr ActionEventStatus(TAction& action, UpdateStatus result) - : action_{&action}, result_{result} {} - - AE_CLASS_COPY_MOVE(ActionEventStatus); - - template - ActionEventStatus OnResult(Handler&& handler) { - if (result_.type == UpdateStatusType::kResult) { - assert(action_ != nullptr); - Invoke(std::forward(handler)); - return {}; - } - return *this; - } - - template - ActionEventStatus OnError(Handler&& handler) { - if (result_.type == UpdateStatusType::kError) { - assert(action_ != nullptr); - Invoke(std::forward(handler)); - return {}; - } - return *this; - } - - template - ActionEventStatus OnStop(Handler&& handler) { - if (result_.type == UpdateStatusType::kStop) { - assert(action_ != nullptr); - Invoke(std::forward(handler)); - return {}; - } - return *this; - } - - TAction& action() const { return *action_; } - UpdateStatus const& result() const { return result_; } - - private: - template - void Invoke(Handler&& handler) { - if constexpr (std::is_invocable_v) { - assert(action_ != nullptr); - std::invoke(std::forward(handler), *action_); - } else { - static_assert(std::is_invocable_v, "Handler must be invocable"); - std::invoke(std::forward(handler)); - } - } - - TAction* action_{nullptr}; - UpdateStatus result_{}; -}; - -/** - * \brief Handler for result type in action event status. - */ -template -struct OnResult { - template - auto operator()(ActionEventStatus action_event_result) { - return action_event_result.OnResult(func); - } - - F func; -}; - -template -OnResult(F&&) -> OnResult; - -/** - * \brief Handler for error type in action event status. - */ -template -struct OnError { - template - auto operator()(ActionEventStatus action_event_result) { - return action_event_result.OnError(func); - } - - F func; -}; - -template -OnError(F&&) -> OnError; - -/** - * \brief Handler for stop type in action event status. - */ -template -struct OnStop { - template - auto operator()(ActionEventStatus action_event_result) { - return action_event_result.OnStop(func); - } - - F func; -}; - -template -OnStop(F&&) -> OnStop; - -/** - * \brief A generic action event result handler. - */ -template -class ActionHandler { - template - struct IsHandler : std::false_type {}; - template - struct IsHandler> : std::true_type {}; - template - struct IsHandler> : std::true_type {}; - template - struct IsHandler> : std::true_type {}; - - template - struct IsSameHandler : std::false_type {}; - template - struct IsSameHandler, OnResult> : std::true_type {}; - template - struct IsSameHandler, OnError> : std::true_type {}; - template - struct IsSameHandler, OnStop> : std::true_type {}; - - template - static constexpr bool HasDuplicate() { - if constexpr (sizeof...(Hs) == 0) { - return false; - } else { - return (IsSameHandler::value || ...) || HasDuplicate(); - } - } - - public: - static_assert(sizeof...(Handlers) > 0, "There must be at least one handler"); - static_assert((IsHandler::value && ...), - "Handlers must be OnResult, OnError or OnStop"); - static_assert(!HasDuplicate(), - "Handlers must not have duplicates"); - - constexpr explicit ActionHandler(Handlers... hdlrs) - : handlers_{std::move(hdlrs)...} {} - - template - void operator()(ActionEventStatus action_event_result) { - std::apply( - [&action_event_result](Handlers&... h) { - ((action_event_result = h(action_event_result)), ...); - }, - handlers_); - } - - private: - std::tuple handlers_; -}; - -} // namespace ae - -#endif // AETHER_ACTIONS_ACTION_EVENT_STATUS_H_ diff --git a/aether/actions/action_pool.h b/aether/actions/action_pool.h new file mode 100644 index 00000000..9b047ca8 --- /dev/null +++ b/aether/actions/action_pool.h @@ -0,0 +1,107 @@ +/* + * 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 AETHER_ACTIONS_ACTION_POLL_H_ +#define AETHER_ACTIONS_ACTION_POLL_H_ + +#include +#include + +#include "aether/warning_disable.h" + +DISABLE_WARNING_PUSH() +IGNORE_IMPLICIT_CONVERSION() +#include +#include +DISABLE_WARNING_POP() + +#include "aether/actions/action.h" +#include "aether/actions/action_context.h" + +namespace ae { +template +class ActionPool : public etl::pool { + public: + static_assert(std::is_base_of_v, "T must be a subclass of Action"); + + using base_t = etl::pool; + + constexpr explicit ActionPool(AC const& ac) noexcept : ac_{ac} {} + ~ActionPool() noexcept { + for (auto i = base_t::begin(); i != base_t::end(); ++i) { + base_t::destroy(&i.template get()); + } + } + + template + T* Create(Args&&... args) { + auto* p = base_t::create(std::forward(args)...); + if (p != nullptr) { + // destroy on finish + static_cast(p)->finished_event().Subscribe( + [this, p]() { Destroy(p); }); + } + return p; + } + + private: + void Destroy(T* p) { + ts_ = ac_.scheduler().Task([&, p]() { base_t::template destroy(p); }); + } + + AC ac_; + TaskSubscription ts_; +}; + +template +class ActionPool, Capacity> + : public etl::variant_pool { + public: + static_assert((std::is_base_of_v && ...), + "T must be a subclass of Action"); + using base_t = etl::variant_pool; + + constexpr explicit ActionPool(AC const& ac) noexcept : ac_{ac} {} + ~ActionPool() noexcept { + for (auto i = base_t::begin(); i != base_t::end(); ++i) { + // destroy each element as an Action + base_t::destroy(&i.template get()); + } + } + + template + requires(std::is_same_v || ...) + U* Create(Args&&... args) { + auto* p = base_t::template create(std::forward(args)...); + if (p != nullptr) { + // destroy on finish + static_cast(p)->finished_event().Subscribe( + [this, p_ = static_cast(p)]() { Destroy(p_); }); + } + return p; + } + + private: + void Destroy(Action* p) { + ts_ = ac_.scheduler().Task([&, p]() { base_t::destroy(p); }); + } + + AC ac_; + TaskSubscription ts_; +}; +} // namespace ae + +#endif // AETHER_ACTIONS_ACTION_POLL_H_ diff --git a/aether/actions/action_processor.cpp b/aether/actions/action_processor.cpp deleted file mode 100644 index 8251d346..00000000 --- a/aether/actions/action_processor.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2024 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. - */ - -#include "aether/actions/action_processor.h" - -#include - -#include "aether/actions/action.h" // IWYU pragma: keep - -namespace ae { -TimePoint ActionProcessor::Update(TimePoint current_time) { - auto next_update = current_time; - - for (auto const& action : ActionRegistryReplica()) { - auto new_time = action->ActionUpdate(current_time); - next_update = SelectNextUpdate(new_time, next_update, current_time); - } - CleanUpActions(); - return next_update; -} - -ActionTrigger& ActionProcessor::get_trigger() { return action_trigger_; } - -ActionRegistry& ActionProcessor::get_registry() { return action_registry_; } - -TimePoint ActionProcessor::SelectNextUpdate(TimePoint new_time, - TimePoint old_time, - TimePoint current_time) { - if (new_time > current_time) { - if (old_time != current_time) { - return std::min(new_time, old_time); - } - return new_time; - } - - return std::max(old_time, current_time); -} - -std::vector -ActionProcessor::ActionRegistryReplica() { - std::vector result; - result.reserve(action_registry_.size()); - for (auto const& a : action_registry_) { - result.emplace_back(a); - } - return result; -} - -void ActionProcessor::CleanUpActions() { - for (auto it = std::begin(action_registry_); - it != std::end(action_registry_);) { - if ((*it)->IsFinished()) { - it = action_registry_.Remove(it); - } else { - ++it; - } - } -} - -} // namespace ae diff --git a/aether/actions/action_processor.h b/aether/actions/action_processor.h deleted file mode 100644 index b74b7701..00000000 --- a/aether/actions/action_processor.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2024 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 AETHER_ACTIONS_ACTION_PROCESSOR_H_ -#define AETHER_ACTIONS_ACTION_PROCESSOR_H_ - -#include "aether/common.h" - -#include "aether/actions/action_trigger.h" -#include "aether/actions/action_registry.h" - -namespace ae { -/** - * \brief Processor for set of actions. - */ -class ActionProcessor { - public: - ActionProcessor() = default; - ~ActionProcessor() = default; - - TimePoint Update(TimePoint current_time); - - ActionTrigger& get_trigger(); - ActionRegistry& get_registry(); - - private: - static TimePoint SelectNextUpdate(TimePoint new_time, TimePoint old_time, - TimePoint current_time); - - std::vector ActionRegistryReplica(); - void CleanUpActions(); - - ActionTrigger action_trigger_; - ActionRegistry action_registry_; -}; -} // namespace ae - -#endif // AETHER_ACTIONS_ACTION_PROCESSOR_H_ diff --git a/aether/actions/action_ptr.h b/aether/actions/action_ptr.h deleted file mode 100644 index 7440f437..00000000 --- a/aether/actions/action_ptr.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2025 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 AETHER_ACTIONS_ACTION_PTR_H_ -#define AETHER_ACTIONS_ACTION_PTR_H_ - -#include - -#include - -#include "aether/common.h" -#include "aether/actions/action_context.h" - -namespace ae { -template -struct ActionStoppable : std::false_type {}; - -template -struct ActionStoppable().Stop())>> - : std::true_type {}; - -/** - * \brief Shared ownership to actions - */ -template -class ActionPtr { - template - friend class ActionPtr; - - public: - ActionPtr() = default; - - template - explicit ActionPtr(ActionContext action_context, TArgs&&... args) - : ptr_{std::make_shared(action_context, - std::forward(args)...)} { - action_context.get_registry().PushBack(ptr_); - } - - template ))> - ActionPtr(ActionPtr const& other) : ptr_(other.ptr_) {} - - template ))> - ActionPtr(ActionPtr&& other) noexcept - : ptr_(std::move(other.ptr_)) {} - - AE_CLASS_COPY_MOVE(ActionPtr) - - template ))> - ActionPtr& operator=(ActionPtr const& other) { - ptr_ = other.ptr_; - return *this; - } - - template ))> - ActionPtr& operator=(ActionPtr&& other) noexcept { - ptr_ = std::move(other.ptr_); - return *this; - } - - TAction& operator*() { return *ptr_; } - TAction const& operator*() const { return *ptr_; } - TAction* operator->() const { return ptr_.get(); } - explicit operator bool() const { return static_cast(ptr_); } - void reset() { ptr_.reset(); } - - private: - std::shared_ptr ptr_; -}; - -template -ActionPtr(ActionPtr&& t) -> ActionPtr; -template -ActionPtr(ActionPtr const& t) -> ActionPtr; - -template -auto MakeActionPtr(ActionContext action_context, TArgs&&... args) { - return ActionPtr(action_context, std::forward(args)...); -} - -template