From 287dbfa25130ead5010877e934effbb0a2534265 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 9 Oct 2024 14:27:54 -0700 Subject: [PATCH] [test] introduce Nexus test framework and platform (#10533) This commit introduces a new test framework named Nexus. The framework includes the Nexus platform implementation that emulates platform behavior, allowing multiple nodes running the OpenThread core stack to be simulated and interact with each other within the same process. Unlike the simulation platform, where nodes run in separate processes and interact via POSIX sockets, Nexus nodes are simulated within a single process. Nexus tests can interact directly with the C++ or C OT core APIs, providing more control than the simulation platform's CLI-based interactions. The flow of time in Nexus tests is directly controlled by the test itself, allowing for quick time interval advancement. This model allows for faster and more scalable simulations, enabling quick simulation of larger networks for longer durations. This commit introduces the basic platform implementation, including: - `nexus_alarm`, `nexus_radio`, and `nexus_settings` modules. - Logging support, allowing logs to be distinguished per emulated node. --- .github/workflows/toranj.yml | 24 ++ CMakeLists.txt | 5 + etc/cmake/options.cmake | 2 +- src/core/instance/instance.hpp | 23 ++ tests/CMakeLists.txt | 6 +- tests/nexus/CMakeLists.txt | 112 +++++++ tests/nexus/README.md | 29 ++ tests/nexus/build.sh | 66 ++++ tests/nexus/openthread-core-nexus-config.h | 133 ++++++++ tests/nexus/platform/nexus_alarm.cpp | 60 ++++ tests/nexus/platform/nexus_alarm.hpp | 48 +++ tests/nexus/platform/nexus_core.cpp | 205 +++++++++++++ tests/nexus/platform/nexus_core.hpp | 94 ++++++ tests/nexus/platform/nexus_misc.cpp | 154 ++++++++++ tests/nexus/platform/nexus_node.cpp | 87 ++++++ tests/nexus/platform/nexus_node.hpp | 92 ++++++ tests/nexus/platform/nexus_radio.cpp | 341 +++++++++++++++++++++ tests/nexus/platform/nexus_radio.hpp | 80 +++++ tests/nexus/platform/nexus_settings.cpp | 160 ++++++++++ tests/nexus/platform/nexus_settings.hpp | 82 +++++ tests/nexus/platform/nexus_utils.hpp | 77 +++++ tests/nexus/test_form_join.cpp | 124 ++++++++ tests/nexus/test_large_network.cpp | 150 +++++++++ 23 files changed, 2152 insertions(+), 2 deletions(-) create mode 100644 tests/nexus/CMakeLists.txt create mode 100644 tests/nexus/README.md create mode 100755 tests/nexus/build.sh create mode 100644 tests/nexus/openthread-core-nexus-config.h create mode 100644 tests/nexus/platform/nexus_alarm.cpp create mode 100644 tests/nexus/platform/nexus_alarm.hpp create mode 100644 tests/nexus/platform/nexus_core.cpp create mode 100644 tests/nexus/platform/nexus_core.hpp create mode 100644 tests/nexus/platform/nexus_misc.cpp create mode 100644 tests/nexus/platform/nexus_node.cpp create mode 100644 tests/nexus/platform/nexus_node.hpp create mode 100644 tests/nexus/platform/nexus_radio.cpp create mode 100644 tests/nexus/platform/nexus_radio.hpp create mode 100644 tests/nexus/platform/nexus_settings.cpp create mode 100644 tests/nexus/platform/nexus_settings.hpp create mode 100644 tests/nexus/platform/nexus_utils.hpp create mode 100644 tests/nexus/test_form_join.cpp create mode 100644 tests/nexus/test_large_network.cpp diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml index bed59676b3e..3d95576fe76 100644 --- a/.github/workflows/toranj.yml +++ b/.github/workflows/toranj.yml @@ -190,6 +190,30 @@ jobs: run: | ./tests/toranj/build.sh posix-15.4 + nexus: + name: nexus + runs-on: ubuntu-20.04 + steps: + - name: Harden Runner + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + submodules: true + - name: Bootstrap + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + run: | + sudo apt-get update + sudo apt-get --no-install-recommends install -y clang-10 clang++-10 ninja-build llvm lcov + sudo apt-get --no-install-recommends install -y g++-multilib + - name: Build & Run + run: | + ./tests/nexus/build.sh + ninja test + upload-coverage: needs: - toranj-cli diff --git a/CMakeLists.txt b/CMakeLists.txt index 54aa143b882..72cfd88815a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,11 @@ if(OT_PLATFORM STREQUAL "posix") add_subdirectory("${PROJECT_SOURCE_DIR}/src/posix/platform") elseif(OT_PLATFORM STREQUAL "external") # skip in this case +elseif(OT_PLATFORM STREQUAL "nexus") + if (OT_APP_CLI OR OT_APP_NCP OR OT_APP_RCP) + message(FATAL_ERROR "no app (cli/ncp/rcp) should be enabled with nexus simulation platform") + endif() + target_compile_definitions(ot-config INTERFACE OPENTHREAD_PLATFORM_NEXUS=1) else() target_include_directories(ot-config INTERFACE ${PROJECT_SOURCE_DIR}/examples/platforms/${OT_PLATFORM}) add_subdirectory("${PROJECT_SOURCE_DIR}/examples/platforms/${OT_PLATFORM}") diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index d85360b1e32..2a2d4c59828 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -265,7 +265,7 @@ message(STATUS "- - - - - - - - - - - - - - - - ") # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Get a list of the available platforms and output as a list to the 'arg_platforms' argument function(ot_get_platforms arg_platforms) - list(APPEND result "NO" "posix" "external") + list(APPEND result "NO" "posix" "external" "nexus") set(platforms_dir "${PROJECT_SOURCE_DIR}/examples/platforms") file(GLOB platforms RELATIVE "${platforms_dir}" "${platforms_dir}/*") foreach(platform IN LISTS platforms) diff --git a/src/core/instance/instance.hpp b/src/core/instance/instance.hpp index 7ad5bc1a033..188609b4b6c 100644 --- a/src/core/instance/instance.hpp +++ b/src/core/instance/instance.hpp @@ -252,6 +252,15 @@ class Instance : public otInstance, private NonCopyable */ uint32_t GetId(void) const { return mId; } +#if OPENTHREAD_PLATFORM_NEXUS + /** + * Sets the instance identifier. + * + * @param[in] aId The identifier to assign to the `Instance`. + */ + void SetId(uint32_t aId) { mId = aId; } +#endif + /** * Indicates whether or not the instance is valid/initialized and not yet finalized. * @@ -412,9 +421,23 @@ class Instance : public otInstance, private NonCopyable */ template inline Type &Get(void); +#if OPENTHREAD_PLATFORM_NEXUS + /** + * Constructor to initialize an `Instance` + */ + Instance(void); + + /** + * Called after constructor initialization. + */ + void AfterInit(void); +#endif + private: +#if !OPENTHREAD_PLATFORM_NEXUS Instance(void); void AfterInit(void); +#endif // Order of variables (their initialization in `Instance`) // is important. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 47424b8ee10..4fde2520839 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,7 +28,7 @@ option(OT_BUILD_GTEST "enable gtest") -if(OT_FTD AND BUILD_TESTING) +if(OT_FTD AND BUILD_TESTING AND (NOT OT_PLATFORM STREQUAL "nexus")) add_subdirectory(unit) if(OT_BUILD_GTEST) add_subdirectory(gtest) @@ -40,3 +40,7 @@ option(OT_FUZZ_TARGETS "enable fuzz targets" OFF) if(OT_FUZZ_TARGETS) add_subdirectory(fuzz) endif() + +if(OT_PLATFORM STREQUAL "nexus") + add_subdirectory(nexus) +endif() diff --git a/tests/nexus/CMakeLists.txt b/tests/nexus/CMakeLists.txt new file mode 100644 index 00000000000..4421d9a444b --- /dev/null +++ b/tests/nexus/CMakeLists.txt @@ -0,0 +1,112 @@ +# +# Copyright (c) 2024, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +set(COMMON_INCLUDES + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/src/core + ${PROJECT_SOURCE_DIR}/tests/nexus/platform +) + +set(COMMON_COMPILE_OPTIONS + -DOPENTHREAD_FTD=1 + -DOPENTHREAD_MTD=0 + -DOPENTHREAD_RADIO=0 +) + +add_library(ot-nexus-platform + platform/nexus_alarm.cpp + platform/nexus_core.cpp + platform/nexus_misc.cpp + platform/nexus_node.cpp + platform/nexus_radio.cpp + platform/nexus_settings.cpp +) + +target_include_directories(ot-nexus-platform + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-nexus-platform + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(ot-nexus-platform + PRIVATE + ot-config + ${OT_MBEDTLS} +) + +set(COMMON_LIBS + ot-nexus-platform + openthread-ftd + ot-nexus-platform + ${OT_MBEDTLS} + ot-config + openthread-ftd +) + +#---------------------------------------------------------------------------------------------------------------------- + +macro(ot_nexus_test name) + + # Macro to add an OpenThread nexus test. + # + # Nexus test name will be `nexus_{name}`. Test source file of + # `test_{name}.cpp` is used. Optional extra arguments can be + # passed to provide additional source files. + + add_executable(nexus_${name} + test_${name}.cpp ${ARGN} + ) + + target_include_directories(nexus_${name} + PRIVATE + ${COMMON_INCLUDES} + ) + + target_link_libraries(nexus_${name} + PRIVATE + ${COMMON_LIBS} + ) + + target_compile_options(nexus_${name} + PRIVATE + ${COMMON_COMPILE_OPTIONS} + ) + + add_test(NAME nexus_${name} COMMAND nexus_${name}) +endmacro() + + +#---------------------------------------------------------------------------------------------------------------------- + +ot_nexus_test(form_join) +ot_nexus_test(large_network) diff --git a/tests/nexus/README.md b/tests/nexus/README.md new file mode 100644 index 00000000000..2e836037b04 --- /dev/null +++ b/tests/nexus/README.md @@ -0,0 +1,29 @@ +# Nexus test framework + +Nexus is a test framework for OpenThread testing. + +### Design Goals + +- **Faster and more scalable network simulation**: Enable faster and more efficient simulations of OpenThread networks involving a large number of nodes over extended durations. +- **Enhanced control**: Achieve greater control and scalability over simulated tests. + +### Features + +- Includes the Nexus platform implementation that emulates platform behavior, allowing multiple nodes running the OpenThread core stack to be simulated and interact with each other within the same process. +- Unlike the simulation platform (under `examples/platforms/simulation`), where nodes run in separate processes and interact via POSIX sockets, Nexus nodes are simulated within a single process. +- Nexus tests can interact directly with the C++ or C OT core APIs, providing more control than the simulation platform's CLI-based interactions. +- The flow of time in Nexus tests is directly controlled by the test itself, allowing for quick time interval advancement. + +### How to build and run tests + +To build Nexus test cases, the `build.sh` script can be used: + +```bash +./tests/nexus/build.sh +``` + +Afterwards, each test can be run directly: + +```bash +./tests/nexus/nexus_form_join +``` diff --git a/tests/nexus/build.sh b/tests/nexus/build.sh new file mode 100755 index 00000000000..738ef2adb58 --- /dev/null +++ b/tests/nexus/build.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# +# Copyright (c) 2024, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +display_usage() +{ + echo "" + echo "Nexus build script " + echo "" + echo "" +} + +die() +{ + echo " *** ERROR: " "$*" + exit 1 +} + +cd "$(dirname "$0")" || die "cd failed" +cd ../.. || die "cd failed" + +if [ -n "${top_builddir}" ]; then + top_srcdir=$(pwd) + mkdir -p "${top_builddir}" +else + top_srcdir=. + top_builddir=. +fi + +echo "====================================================================================================" +echo "Building OpenThread Nexus test platform" +echo "====================================================================================================" +cd "${top_builddir}" || die "cd failed" +cmake -GNinja -DOT_PLATFORM=nexus -DOT_COMPILE_WARNING_AS_ERROR=ON \ + -DOT_MULTIPLE_INSTANCE=ON \ + -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \ + -DOT_PROJECT_CONFIG=../tests/nexus/openthread-core-nexus-config.h \ + "${top_srcdir}" || die +ninja || die + +exit 0 diff --git a/tests/nexus/openthread-core-nexus-config.h b/tests/nexus/openthread-core-nexus-config.h new file mode 100644 index 00000000000..8d0cb3bb193 --- /dev/null +++ b/tests/nexus/openthread-core-nexus-config.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * This header file defines the OpenThread core configuration for Nexus simulation + * + */ + +#ifndef OT_CORE_NEXUS_CONFIG_H_ +#define OT_CORE_NEXUS_CONFIG_H_ + +#ifndef OPENTHREAD_RADIO +#define OPENTHREAD_RADIO 0 +#endif + +#ifndef OPENTHREAD_RADIO +#define OPENTHREAD_RADIO_CLI 0 +#endif + +#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1 +#define OPENTHREAD_CONFIG_BLE_TCAT_ENABLE 0 +#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1 +#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 1 +#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1 +#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1 +#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 0 +#define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 0 +#define OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE 0 +#define OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE 1 +#define OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE 1 +#define OPENTHREAD_CONFIG_COAP_API_ENABLE 1 +#define OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE 1 +#define OPENTHREAD_CONFIG_COMMISSIONER_ENABLE 1 +#define OPENTHREAD_CONFIG_COMMISSIONER_MAX_JOINER_ENTRIES 4 +#define OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE 1 +#define OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE 1 +#define OPENTHREAD_CONFIG_DIAG_ENABLE 0 +#define OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE 1 +#define OPENTHREAD_CONFIG_DNS_DSO_ENABLE 0 +#define OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE 0 +#define OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE 1 +#define OPENTHREAD_CONFIG_ECDSA_ENABLE 1 +#define OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE 1 +#define OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE 1 +#define OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 1 +#define OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS 4 +#define OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS 8 +#define OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE 1 +#define OPENTHREAD_CONFIG_IP6_SLAAC_NUM_ADDRESSES 4 +#define OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE 1 +#define OPENTHREAD_CONFIG_JOINER_ENABLE 1 +#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_INFO +#define OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE 1 +#define OPENTHREAD_CONFIG_LOG_LEVEL_INIT OT_LOG_LEVEL_CRIT +#define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED +#define OPENTHREAD_CONFIG_LOG_PLATFORM 0 +#define OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL 1 +#define OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME 0 +#define OPENTHREAD_CONFIG_LOG_SUFFIX "" +#define OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE 0 +#define OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE 0 +#define OPENTHREAD_CONFIG_MAC_FILTER_ENABLE 1 +#define OPENTHREAD_CONFIG_MAC_FILTER_SIZE 80 +#define OPENTHREAD_CONFIG_MESH_DIAG_ENABLE 1 +#define OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE 1 +#define OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE 1 +#define OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH 1 +#define OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD 10 +#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE 0 +#define OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE 0 +#define OPENTHREAD_CONFIG_MLE_MAX_CHILDREN 128 +#define OPENTHREAD_CONFIG_MULTICAST_DNS_AUTO_ENABLE_ON_INFRA_IF 0 +#define OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE 0 +#define OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE 0 +#define OPENTHREAD_CONFIG_MULTICAST_DNS_PUBLIC_API_ENABLE 0 +#define OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE 0 +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE OPENTHREAD_FTD +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL "Nexus Simulation" +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME "OpenThread by Google Nest" +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION "OT-simul-nexus" +#define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE 1 +#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 256 +#define OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION 0 +#define OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE 0 +#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 0 +#define OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE 0 +#define OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE 0 +#define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE 0 +#define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 0 +#define OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE 1 +#define OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE 0 +#define OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE 1 +#define OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE 0 +#define OPENTHREAD_CONFIG_SRP_SERVER_ENABLE 1 +#define OPENTHREAD_CONFIG_TCP_ENABLE 0 +#define OPENTHREAD_CONFIG_TLS_ENABLE 0 +#define OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES 256 +#define OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_MAX_SNOOP_ENTRIES 16 +#define OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_INITIAL_RETRY_DELAY 4 +#define OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY 120 +#define OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_TIMEOUT 6 +#define OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE 1 +#define OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE 1 +#define OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE 1 +#define OPENTHREAD_CONFIG_TMF_SNOOP_CACHE_ENTRY_TIMEOUT 3 +#define OPENTHREAD_CONFIG_UPTIME_ENABLE 1 + +#endif /* OT_CORE_NEXUS_CONFIG_H_ */ diff --git a/tests/nexus/platform/nexus_alarm.cpp b/tests/nexus/platform/nexus_alarm.cpp new file mode 100644 index 00000000000..673f8652848 --- /dev/null +++ b/tests/nexus/platform/nexus_alarm.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "nexus_alarm.hpp" +#include "nexus_core.hpp" +#include "nexus_node.hpp" + +namespace ot { +namespace Nexus { + +//--------------------------------------------------------------------------------------------------------------------- +// otPlatAlarmMilli APIs + +extern "C" { + +uint32_t otPlatAlarmMilliGetNow(void) { return Core::Get().GetNow().GetValue(); } + +void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) +{ + Alarm &alarm = AsNode(aInstance).mAlarm; + + alarm.mScheduled = true; + alarm.mAlarmTime.SetValue(aT0 + aDt); + + Core::Get().UpdateNextAlarmTime(alarm); +} + +void otPlatAlarmMilliStop(otInstance *aInstance) { AsNode(aInstance).mAlarm.mScheduled = false; } + +} // extern "C" + +} // namespace Nexus +} // namespace ot diff --git a/tests/nexus/platform/nexus_alarm.hpp b/tests/nexus/platform/nexus_alarm.hpp new file mode 100644 index 00000000000..baef030b38c --- /dev/null +++ b/tests/nexus/platform/nexus_alarm.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OT_NEXUS_ALARM_HPP_ +#define OT_NEXUS_ALARM_HPP_ + +#include "instance/instance.hpp" + +namespace ot { +namespace Nexus { + +struct Alarm +{ + bool ShouldTrigger(Time aNow) const { return mScheduled && (aNow >= mAlarmTime); } + + bool mScheduled; + Time mAlarmTime; +}; + +} // namespace Nexus +} // namespace ot + +#endif // OT_NEXUS_ALARM_HPP_ diff --git a/tests/nexus/platform/nexus_core.cpp b/tests/nexus/platform/nexus_core.cpp new file mode 100644 index 00000000000..37b7eef9893 --- /dev/null +++ b/tests/nexus/platform/nexus_core.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "nexus_core.hpp" +#include "nexus_node.hpp" + +namespace ot { +namespace Nexus { + +Core *Core::sCore = nullptr; + +Core::Core(void) + : mNow(0) + , mCurNodeId(0) + , mPendingAction(false) +{ + VerifyOrQuit(sCore == nullptr); + sCore = this; + + mNextAlarmTime = mNow.GetDistantFuture(); +} + +Node &Core::CreateNode(void) +{ + Node *node; + + node = Node::Allocate(); + VerifyOrQuit(node != nullptr); + + node->GetInstance().SetId(mCurNodeId++); + + mNodes.Push(*node); + + node->GetInstance().AfterInit(); + + return *node; +} + +void Core::UpdateNextAlarmTime(const Alarm &aAlarm) +{ + if (aAlarm.mScheduled) + { + mNextAlarmTime = Min(mNextAlarmTime, Max(mNow, aAlarm.mAlarmTime)); + } +} + +void Core::AdvanceTime(uint32_t aDuration) +{ + TimeMilli targetTime = mNow + aDuration; + + while (mPendingAction || (mNextAlarmTime <= targetTime)) + { + mNextAlarmTime = mNow.GetDistantFuture(); + mPendingAction = false; + + for (Node &node : mNodes) + { + Process(node); + UpdateNextAlarmTime(node.mAlarm); + } + + if (!mPendingAction) + { + mNow = Min(mNextAlarmTime, targetTime); + } + } + + mNow = targetTime; +} + +void Core::Process(Node &aNode) +{ + otTaskletsProcess(&aNode.GetInstance()); + + ProcessRadio(aNode); + + if (aNode.mAlarm.ShouldTrigger(mNow)) + { + otPlatAlarmMilliFired(&aNode.GetInstance()); + } +} + +void Core::ProcessRadio(Node &aNode) +{ + Mac::Address dstAddr; + uint16_t dstPanId; + bool ackRequested; + AckMode ackMode = kNoAck; + + VerifyOrExit(aNode.mRadio.mState == Radio::kStateTransmit); + + if (aNode.mRadio.mTxFrame.GetDstAddr(dstAddr) != kErrorNone) + { + dstAddr.SetNone(); + } + + if (aNode.mRadio.mTxFrame.GetDstPanId(dstPanId) != kErrorNone) + { + dstPanId = Mac::kPanIdBroadcast; + } + + ackRequested = aNode.mRadio.mTxFrame.GetAckRequest(); + + otPlatRadioTxStarted(&aNode.GetInstance(), &aNode.mRadio.mTxFrame); + + for (Node &rxNode : mNodes) + { + bool matchesDst; + + if ((&rxNode == &aNode) || !rxNode.mRadio.CanReceiveOnChannel(aNode.mRadio.mTxFrame.GetChannel())) + { + continue; + } + + matchesDst = rxNode.mRadio.Matches(dstAddr, dstPanId); + + if (matchesDst || rxNode.mRadio.mPromiscuous) + { + // `rxNode` should receive this frame. + + Radio::Frame rxFrame(aNode.mRadio.mTxFrame); + + rxFrame.mInfo.mRxInfo.mTimestamp = (mNow.GetValue() * 1000u); + rxFrame.mInfo.mRxInfo.mRssi = kDefaultRxRssi; + rxFrame.mInfo.mRxInfo.mLqi = 0; + + if (matchesDst && !dstAddr.IsNone() && !dstAddr.IsBroadcast() && ackRequested) + { + Mac::Address srcAddr; + + ackMode = kSendAckNoFramePending; + + if ((aNode.mRadio.mTxFrame.GetSrcAddr(srcAddr) == kErrorNone) && + rxNode.mRadio.HasFramePendingFor(srcAddr)) + { + ackMode = kSendAckFramePending; + rxFrame.mInfo.mRxInfo.mAckedWithFramePending = true; + } + } + + otPlatRadioReceiveDone(&rxNode.GetInstance(), &rxFrame, kErrorNone); + } + + if (ackMode != kNoAck) + { + // No need to go through rest of `mNodes` + // if already acked by a node. + break; + } + } + + aNode.mRadio.mChannel = aNode.mRadio.mTxFrame.mChannel; + aNode.mRadio.mState = Radio::kStateReceive; + + if (ackMode != kNoAck) + { + Mac::TxFrame ackFrame; + uint8_t ackPsdu[Mac::Frame::kImmAckLength]; + + ClearAllBytes(ackFrame); + ackFrame.mPsdu = ackPsdu; + + ackFrame.GenerateImmAck( + static_cast(static_cast(aNode.mRadio.mTxFrame)), + (ackMode == kSendAckFramePending)); + + otPlatRadioTxDone(&aNode.GetInstance(), &aNode.mRadio.mTxFrame, &ackFrame, kErrorNone); + } + else + { + otPlatRadioTxDone(&aNode.GetInstance(), &aNode.mRadio.mTxFrame, nullptr, + ackRequested ? kErrorNoAck : kErrorNone); + } + +exit: + return; +} + +} // namespace Nexus +} // namespace ot diff --git a/tests/nexus/platform/nexus_core.hpp b/tests/nexus/platform/nexus_core.hpp new file mode 100644 index 00000000000..f164e5bc33a --- /dev/null +++ b/tests/nexus/platform/nexus_core.hpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OT_NEXUS_CORE_HPP_ +#define OT_NEXUS_CORE_HPP_ + +#include "common/owning_list.hpp" +#include "instance/instance.hpp" + +#include "nexus_alarm.hpp" +#include "nexus_radio.hpp" +#include "nexus_utils.hpp" + +namespace ot { +namespace Nexus { + +class Node; + +class Core +{ +public: + Core(void); + + static Core &Get(void) { return *sCore; } + + Node &CreateNode(void); + LinkedList &GetNodes(void) { return mNodes; } + + TimeMilli GetNow(void) { return mNow; } + void AdvanceTime(uint32_t aDuration); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Used by platform implementation + + void SetActiveNode(Node *aNode) { mActiveNode = aNode; } + Node *GetActiveNode(void) { return mActiveNode; } + + void UpdateNextAlarmTime(const Alarm &aAlarm); + void MarkPendingAction(void) { mPendingAction = true; } + +private: + static constexpr int8_t kDefaultRxRssi = -20; + + enum AckMode : uint8_t + { + kNoAck, + kSendAckNoFramePending, + kSendAckFramePending, + }; + + void Process(Node &aNode); + void ProcessRadio(Node &aNode); + + static Core *sCore; + + OwningList mNodes; + uint16_t mCurNodeId; + bool mPendingAction; + TimeMilli mNow; + TimeMilli mNextAlarmTime; + Node *mActiveNode; +}; + +void Log(const char *aFormat, ...); + +} // namespace Nexus +} // namespace ot + +#endif // OT_NEXUS_CORE_HPP_ diff --git a/tests/nexus/platform/nexus_misc.cpp b/tests/nexus/platform/nexus_misc.cpp new file mode 100644 index 00000000000..09919940f36 --- /dev/null +++ b/tests/nexus/platform/nexus_misc.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include "nexus_core.hpp" +#include "nexus_node.hpp" + +namespace ot { +namespace Nexus { + +static void LogVarArgs(Node *aActiveNode, const char *aFormat, va_list aArgs); + +extern "C" { + +//--------------------------------------------------------------------------------------------------------------------- +// otTasklets + +void otTaskletsSignalPending(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + Core::Get().MarkPendingAction(); +} + +//--------------------------------------------------------------------------------------------------------------------- +// otPlatLog + +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + OT_UNUSED_VARIABLE(aLogLevel); + OT_UNUSED_VARIABLE(aLogRegion); + + va_list args; + + va_start(args, aFormat); + LogVarArgs(Core::Get().GetActiveNode(), aFormat, args); + va_end(args); +} + +//--------------------------------------------------------------------------------------------------------------------- +// Heap allocation APIs + +void *otPlatCAlloc(size_t aNum, size_t aSize) +{ + void *ptr = calloc(aNum, aSize); + return ptr; +} + +void otPlatFree(void *aPtr) { free(aPtr); } + +//--------------------------------------------------------------------------------------------------------------------- +// Entropy + +otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength) +{ + Error error = OT_ERROR_NONE; + FILE *file = nullptr; + size_t readLength; + + file = fopen("/dev/urandom", "rb"); + VerifyOrExit(file != nullptr, error = kErrorFailed); + + readLength = fread(aOutput, 1, aOutputLength, file); + + if (readLength != aOutputLength) + { + error = kErrorFailed; + } + + fclose(file); + +exit: + return error; +} + +//--------------------------------------------------------------------------------------------------------------------- +// Misc + +otError otPlatDiagProcess(otInstance *, uint8_t, char *[]) { return kErrorNotImplemented; } +void otPlatDiagModeSet(bool) {} +bool otPlatDiagModeGet() { return false; } +void otPlatDiagChannelSet(uint8_t) {} +void otPlatDiagTxPowerSet(int8_t) {} +void otPlatDiagRadioReceived(otInstance *, otRadioFrame *, otError) {} +void otPlatDiagAlarmCallback(otInstance *) {} +void otPlatUartSendDone(void) {} +void otPlatUartReceived(const uint8_t *, uint16_t) {} +void otPlatReset(otInstance *) {} +otError otPlatResetToBootloader(otInstance *) { return kErrorNotImplemented; } +otPlatResetReason otPlatGetResetReason(otInstance *) { return OT_PLAT_RESET_REASON_POWER_ON; } +void otPlatWakeHost(void) {} + +} // extern "C" + +//--------------------------------------------------------------------------------------------------------------------- +// Log related function + +void Log(const char *aFormat, ...) +{ + va_list args; + + va_start(args, aFormat); + LogVarArgs(nullptr, aFormat, args); + va_end(args); +} + +static void LogVarArgs(Node *aActiveNode, const char *aFormat, va_list aArgs) +{ + uint32_t now = Core::Get().GetNow().GetValue(); + + printf("%02u:%02u:%02u.%03u ", now / 3600000, (now / 60000) % 60, (now / 1000) % 60, now % 1000); + + if (aActiveNode != nullptr) + { + printf("%03u ", aActiveNode->GetInstance().GetId()); + } + + vprintf(aFormat, aArgs); + printf("\n"); +} + +} // namespace Nexus +} // namespace ot diff --git a/tests/nexus/platform/nexus_node.cpp b/tests/nexus/platform/nexus_node.cpp new file mode 100644 index 00000000000..23e4e37dfdd --- /dev/null +++ b/tests/nexus/platform/nexus_node.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "nexus_node.hpp" +#include "nexus_utils.hpp" + +namespace ot { +namespace Nexus { + +void Node::Form(void) +{ + MeshCoP::Dataset::Info datasetInfo; + + SuccessOrQuit(datasetInfo.GenerateRandom(*this)); + Get().SaveLocal(datasetInfo); + + Get().Up(); + SuccessOrQuit(Get().Start()); +} + +void Node::Join(Node &aNode, JoinMode aJoinMode) +{ + MeshCoP::Dataset dataset; + Mle::DeviceMode mode(0); + + switch (aJoinMode) + { + case kAsFed: + SuccessOrQuit(Get().SetRouterEligible(false)); + OT_FALL_THROUGH; + + case kAsFtd: + mode.Set(Mle::DeviceMode::kModeRxOnWhenIdle | Mle::DeviceMode::kModeFullThreadDevice | + Mle::DeviceMode::kModeFullNetworkData); + break; + case kAsMed: + mode.Set(Mle::DeviceMode::kModeRxOnWhenIdle | Mle::DeviceMode::kModeFullNetworkData); + break; + case kAsSed: + mode.Set(Mle::DeviceMode::kModeFullNetworkData); + break; + } + + SuccessOrQuit(Get().SetDeviceMode(mode)); + + SuccessOrQuit(aNode.Get().Read(dataset)); + Get().SaveLocal(dataset); + + Get().Up(); + SuccessOrQuit(Get().Start()); +} + +void Node::AllowList(Node &aNode) +{ + SuccessOrQuit(Get().AddAddress(aNode.Get().GetExtAddress())); + Get().SetMode(Mac::Filter::kModeAllowlist); +} + +void Node::UnallowList(Node &aNode) { Get().RemoveAddress(aNode.Get().GetExtAddress()); } + +} // namespace Nexus +} // namespace ot diff --git a/tests/nexus/platform/nexus_node.hpp b/tests/nexus/platform/nexus_node.hpp new file mode 100644 index 00000000000..770332a3f63 --- /dev/null +++ b/tests/nexus/platform/nexus_node.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OT_NEXUS_NODE_HPP_ +#define OT_NEXUS_NODE_HPP_ + +#include "instance/instance.hpp" + +#include "nexus_alarm.hpp" +#include "nexus_core.hpp" +#include "nexus_radio.hpp" +#include "nexus_settings.hpp" +#include "nexus_utils.hpp" + +namespace ot { +namespace Nexus { + +class Node : public Heap::Allocatable, public LinkedListEntry, private Instance +{ + friend class Heap::Allocatable; + +public: + enum JoinMode : uint8_t + { + kAsFtd, + kAsFed, + kAsMed, + kAsSed, + }; + + void Form(void); + void Join(Node &aNode, JoinMode aJoinMode = kAsFtd); + void AllowList(Node &aNode); + void UnallowList(Node &aNode); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + template Type &Get(void) + { + Core::Get().SetActiveNode(this); + return Instance::Get(); + } + + Instance &GetInstance(void) + { + Core::Get().SetActiveNode(this); + return *this; + } + + static Node &From(otInstance *aInstance) { return static_cast(*aInstance); } + + Node *mNext; + Radio mRadio; + Alarm mAlarm; + Settings mSettings; + bool mPendingTasklet; + +private: + Node(void) = default; +}; + +inline Node &AsNode(otInstance *aInstance) { return Node::From(aInstance); } + +} // namespace Nexus +} // namespace ot + +#endif // OT_NEXUS_NODE_HPP_ diff --git a/tests/nexus/platform/nexus_radio.cpp b/tests/nexus/platform/nexus_radio.cpp new file mode 100644 index 00000000000..5a94c771ba2 --- /dev/null +++ b/tests/nexus/platform/nexus_radio.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "nexus_core.hpp" +#include "nexus_node.hpp" + +namespace ot { +namespace Nexus { + +//--------------------------------------------------------------------------------------------------------------------- +// otPlatRadio APIs + +extern "C" { + +otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_RADIO_CAPS_NONE; +} + +int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return Radio::kRadioSensetivity; +} + +void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) +{ + uint32_t nodeId = AsNode(aInstance).GetInstance().GetId(); + + memset(aIeeeEui64, 0, sizeof(Mac::ExtAddress)); + + aIeeeEui64[6] = (nodeId >> 8) & 0xff; + aIeeeEui64[7] = nodeId & 0xff; +} + +void otPlatRadioSetPanId(otInstance *aInstance, otPanId aPanId) { AsNode(aInstance).mRadio.mPanId = aPanId; } + +void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aExtAddress) +{ + AsNode(aInstance).mRadio.mExtAddress.Set(aExtAddress->m8, Mac::ExtAddress::kReverseByteOrder); +} + +void otPlatRadioSetShortAddress(otInstance *aInstance, otShortAddress aShortAddress) +{ + AsNode(aInstance).mRadio.mShortAddress = aShortAddress; +} + +bool otPlatRadioGetPromiscuous(otInstance *aInstance) { return AsNode(aInstance).mRadio.mPromiscuous; } + +void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) { AsNode(aInstance).mRadio.mPromiscuous = aEnable; } + +otRadioState otPlatRadioGetState(otInstance *aInstance) { return AsNode(aInstance).mRadio.mState; } + +otError otPlatRadioEnable(otInstance *aInstance) +{ + Error error = kErrorNone; + Radio &radio = AsNode(aInstance).mRadio; + + VerifyOrExit(radio.mState == Radio::kStateDisabled, error = kErrorFailed); + radio.mState = Radio::kStateSleep; + +exit: + return error; +} + +otError otPlatRadioDisable(otInstance *aInstance) +{ + AsNode(aInstance).mRadio.mState = Radio::kStateDisabled; + return kErrorNone; +} + +bool otPlatRadioIsEnabled(otInstance *aInstance) { return AsNode(aInstance).mRadio.mState != Radio::kStateDisabled; } + +otError otPlatRadioSleep(otInstance *aInstance) +{ + Error error = kErrorNone; + Radio &radio = AsNode(aInstance).mRadio; + + VerifyOrExit(radio.mState != Radio::kStateDisabled, error = kErrorInvalidState); + VerifyOrExit(radio.mState != Radio::kStateTransmit, error = kErrorBusy); + radio.mState = Radio::kStateSleep; + +exit: + return error; +} + +otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) +{ + Error error = kErrorNone; + Radio &radio = AsNode(aInstance).mRadio; + + VerifyOrExit(radio.mState != Radio::kStateDisabled, error = kErrorInvalidState); + radio.mState = Radio::kStateReceive; + radio.mChannel = aChannel; + +exit: + return error; +} + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) { return &AsNode(aInstance).mRadio.mTxFrame; } + +otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) +{ + Error error = kErrorNone; + Radio &radio = AsNode(aInstance).mRadio; + + VerifyOrExit(radio.mState == Radio::kStateReceive, error = kErrorInvalidState); + OT_ASSERT(aFrame == &AsNode(aInstance).mRadio.mTxFrame); + radio.mState = Radio::kStateTransmit; + + Core::Get().MarkPendingAction(); + +exit: + return error; +} + +int8_t otPlatRadioGetRssi(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return Radio::kRadioSensetivity; +} + +void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) +{ + AsNode(aInstance).mRadio.mSrcMatchEnabled = aEnable; +} + +otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, otShortAddress aShortAddress) +{ + Error error = kErrorNone; + Radio &radio = AsNode(aInstance).mRadio; + + VerifyOrExit(!radio.mSrcMatchShortEntries.Contains(aShortAddress)); + error = radio.mSrcMatchShortEntries.PushBack(aShortAddress); + +exit: + return error; +} + +otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) +{ + Error error = kErrorNone; + Radio &radio = AsNode(aInstance).mRadio; + Mac::ExtAddress extAddress; + + extAddress.Set(aExtAddress->m8, Mac::ExtAddress::kReverseByteOrder); + + VerifyOrExit(!radio.mSrcMatchExtEntries.Contains(extAddress)); + error = radio.mSrcMatchExtEntries.PushBack(extAddress); + +exit: + return error; +} + +otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, otShortAddress aShortAddress) +{ + Error error = kErrorNone; + Radio &radio = AsNode(aInstance).mRadio; + uint16_t *entry; + + entry = radio.mSrcMatchShortEntries.Find(aShortAddress); + VerifyOrExit(entry != nullptr, error = kErrorNoAddress); + + radio.mSrcMatchShortEntries.Remove(*entry); + +exit: + return error; +} + +otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) +{ + Error error = kErrorNone; + Radio &radio = AsNode(aInstance).mRadio; + Mac::ExtAddress extAddress; + Mac::ExtAddress *entry; + + extAddress.Set(aExtAddress->m8, Mac::ExtAddress::kReverseByteOrder); + + entry = radio.mSrcMatchExtEntries.Find(extAddress); + VerifyOrExit(entry != nullptr, error = kErrorNoAddress); + + radio.mSrcMatchExtEntries.Remove(*entry); + +exit: + return error; +} + +void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) +{ + AsNode(aInstance).mRadio.mSrcMatchShortEntries.Clear(); +} + +void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) { AsNode(aInstance).mRadio.mSrcMatchExtEntries.Clear(); } + +// Not supported + +otError otPlatRadioEnergyScan(otInstance *, uint8_t, uint16_t) { return kErrorNotImplemented; } + +otError otPlatRadioGetTransmitPower(otInstance *, int8_t *) { return kErrorNotImplemented; } +otError otPlatRadioSetTransmitPower(otInstance *, int8_t) { return kErrorNotImplemented; } +otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *, int8_t *) { return kErrorNotImplemented; } +otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *, int8_t) { return kErrorNotImplemented; } +otError otPlatRadioGetFemLnaGain(otInstance *, int8_t *) { return kErrorNotImplemented; } +otError otPlatRadioSetFemLnaGain(otInstance *, int8_t) { return kErrorNotImplemented; } +bool otPlatRadioIsCoexEnabled(otInstance *) { return false; } +otError otPlatRadioSetCoexEnabled(otInstance *, bool) { return kErrorNotImplemented; } +otError otPlatRadioGetCoexMetrics(otInstance *, otRadioCoexMetrics *) { return kErrorNotImplemented; } +otError otPlatRadioEnableCsl(otInstance *, uint32_t, otShortAddress, const otExtAddress *) { return kErrorNone; } +otError otPlatRadioResetCsl(otInstance *) { return kErrorNotImplemented; } +void otPlatRadioUpdateCslSampleTime(otInstance *, uint32_t) {} +uint8_t otPlatRadioGetCslAccuracy(otInstance *) { return 0; } +otError otPlatRadioSetChannelTargetPower(otInstance *, uint8_t, int16_t) { return kErrorNotImplemented; } +otError otPlatRadioClearCalibratedPowers(otInstance *) { return kErrorNotImplemented; } +otError otPlatRadioAddCalibratedPower(otInstance *, uint8_t, int16_t, const uint8_t *, uint16_t) +{ + return kErrorNotImplemented; +} + +} // extern "C" + +//--------------------------------------------------------------------------------------------------------------------- +// Radio + +bool Radio::CanReceiveOnChannel(uint8_t aChannel) const +{ + bool canRx = false; + + switch (mState) + { + case kStateReceive: + case kStateTransmit: + break; + default: + ExitNow(); + } + + VerifyOrExit(mChannel == aChannel); + canRx = true; + +exit: + return canRx; +} + +bool Radio::Matches(const Mac::Address &aAddress, Mac::PanId aPanId) const +{ + bool matches = false; + + if (aAddress.IsShort()) + { + VerifyOrExit(aAddress.IsBroadcast() || aAddress.GetShort() == mShortAddress); + } + else if (aAddress.IsExtended()) + { + VerifyOrExit(aAddress.GetExtended() == mExtAddress); + } + + if ((aPanId != Mac::kPanIdBroadcast) && (mPanId != Mac::kPanIdBroadcast)) + { + VerifyOrExit(mPanId == aPanId); + } + + matches = true; + +exit: + return matches; +} + +bool Radio::HasFramePendingFor(const Mac::Address &aAddress) const +{ + bool hasPending = false; + + if (!mSrcMatchEnabled) + { + // Always mark frame pending when `SrcMatch` is disabled. + hasPending = true; + ExitNow(); + } + + if (aAddress.IsShort()) + { + hasPending = mSrcMatchShortEntries.Contains(aAddress.GetShort()); + } + else if (aAddress.IsExtended()) + { + hasPending = mSrcMatchExtEntries.Contains(aAddress.GetExtended()); + } + +exit: + return hasPending; +} + +//--------------------------------------------------------------------------------------------------------------------- +// Radio::Frame + +Radio::Frame::Frame(void) +{ + ClearAllBytes(*this); + mPsdu = &mPsduBuffer[0]; +} + +Radio::Frame::Frame(const Frame &aFrame) +{ + ClearAllBytes(*this); + mPsdu = &mPsduBuffer[0]; + + mLength = aFrame.mLength; + mChannel = aFrame.mChannel; + mRadioType = aFrame.mRadioType; + memcpy(mPsdu, aFrame.mPsdu, mLength); +} + +} // namespace Nexus +} // namespace ot diff --git a/tests/nexus/platform/nexus_radio.hpp b/tests/nexus/platform/nexus_radio.hpp new file mode 100644 index 00000000000..d3857fbe25b --- /dev/null +++ b/tests/nexus/platform/nexus_radio.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OT_NEXUS_RADIO_HPP_ +#define OT_NEXUS_RADIO_HPP_ + +#include "instance/instance.hpp" + +namespace ot { +namespace Nexus { + +struct Radio +{ + static constexpr uint16_t kMaxFrameSize = OT_RADIO_FRAME_MAX_SIZE; + + static constexpr uint16_t kMaxSrcMaatchShort = 80; + static constexpr uint16_t kMaxSrcMatchExt = 10; + + static constexpr int8_t kRadioSensetivity = -100; + + using State = otRadioState; + + static constexpr State kStateDisabled = OT_RADIO_STATE_DISABLED; + static constexpr State kStateSleep = OT_RADIO_STATE_SLEEP; + static constexpr State kStateReceive = OT_RADIO_STATE_RECEIVE; + static constexpr State kStateTransmit = OT_RADIO_STATE_TRANSMIT; + + struct Frame : public Mac::Frame + { + Frame(void); + explicit Frame(const Frame &aFrame); + + uint8_t mPsduBuffer[kMaxFrameSize]; + }; + + bool CanReceiveOnChannel(uint8_t aChannel) const; + bool Matches(const Mac::Address &aAddress, Mac::PanId aPanId) const; + bool HasFramePendingFor(const Mac::Address &aAddress) const; + + State mState; + bool mPromiscuous : 1; + bool mSrcMatchEnabled : 1; + uint8_t mChannel; + Mac::PanId mPanId; + Mac::ShortAddress mShortAddress; + Mac::ExtAddress mExtAddress; + Frame mTxFrame; + Array mSrcMatchShortEntries; + Array mSrcMatchExtEntries; +}; + +} // namespace Nexus +} // namespace ot + +#endif // OT_NEXUS_RADIO_HPP_ diff --git a/tests/nexus/platform/nexus_settings.cpp b/tests/nexus/platform/nexus_settings.cpp new file mode 100644 index 00000000000..032ad185219 --- /dev/null +++ b/tests/nexus/platform/nexus_settings.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "nexus_node.hpp" +#include "nexus_settings.hpp" + +namespace ot { +namespace Nexus { + +//--------------------------------------------------------------------------------------------------------------------- +// otPlatSettings APIs + +extern "C" { + +void otPlatSettingsInit(otInstance *, const uint16_t *, uint16_t) {} +void otPlatSettingsDeinit(otInstance *) {} + +otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) +{ + return AsNode(aInstance).mSettings.Get(aKey, aIndex, aValue, aValueLength); +} + +otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + return AsNode(aInstance).mSettings.SetOrAdd(Settings::kSet, aKey, aValue, aValueLength); +} + +otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + return AsNode(aInstance).mSettings.SetOrAdd(Settings::kAdd, aKey, aValue, aValueLength); +} + +otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) +{ + return AsNode(aInstance).mSettings.Delete(aKey, aIndex); +} + +void otPlatSettingsWipe(otInstance *aInstance) { AsNode(aInstance).mSettings.Wipe(); } + +} // extern "C" + +//--------------------------------------------------------------------------------------------------------------------- +// Settings + +Error Settings::Get(uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) const +{ + Error error = kErrorNone; + const Entry *entry; + const Entry::Value *value; + IndexMatcher IndexMatcher(aIndex); + + entry = mEntries.FindMatching(aKey); + VerifyOrExit(entry != nullptr, error = kErrorNotFound); + + value = entry->mValues.FindMatching(IndexMatcher); + VerifyOrExit(value != nullptr, error = kErrorNotFound); + + if (aValueLength != nullptr) + { + uint16_t size = *aValueLength; + uint16_t length = value->mData.GetLength(); + + *aValueLength = length; + + if (aValue != nullptr) + { + memcpy(aValue, value->mData.GetBytes(), Min(size, length)); + } + } + +exit: + return error; +} + +Error Settings::SetOrAdd(SetAddMode aMode, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) +{ + Entry *entry; + Entry::Value *value; + + entry = mEntries.FindMatching(aKey); + + if (entry == nullptr) + { + entry = Settings::Entry::Allocate(); + VerifyOrQuit(entry != nullptr); + entry->mKey = aKey; + mEntries.Push(*entry); + } + + value = Entry::Value::Allocate(); + VerifyOrQuit(value != nullptr); + SuccessOrQuit(value->mData.SetFrom(aValue, aValueLength)); + + if (aMode == kSet) + { + entry->mValues.Clear(); + } + + entry->mValues.Push(*value); + + return kErrorNone; +} + +Error Settings::Delete(uint16_t aKey, int aIndex) +{ + Error error = kErrorNone; + Entry *entry; + Entry::Value *preValue; + + entry = mEntries.FindMatching(aKey); + VerifyOrExit(entry != nullptr, error = kErrorNotFound); + + if (aIndex < 0) + { + mEntries.RemoveMatching(aKey); + } + else + { + IndexMatcher indexMatcher(aIndex); + OwnedPtr valuePtr; + + valuePtr = entry->mValues.RemoveMatching(indexMatcher); + VerifyOrExit(valuePtr != nullptr, error = kErrorNotFound); + } + +exit: + return error; +} + +void Settings::Wipe(void) { mEntries.Clear(); } + +} // namespace Nexus +} // namespace ot diff --git a/tests/nexus/platform/nexus_settings.hpp b/tests/nexus/platform/nexus_settings.hpp new file mode 100644 index 00000000000..1dfaa629472 --- /dev/null +++ b/tests/nexus/platform/nexus_settings.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OT_NEXUS_SETTINGS_HPP_ +#define OT_NEXUS_SETTINGS_HPP_ + +#include "common/const_cast.hpp" +#include "common/owning_list.hpp" +#include "instance/instance.hpp" + +namespace ot { +namespace Nexus { + +struct Settings +{ + enum SetAddMode : uint8_t + { + kSet, + kAdd, + }; + + Error Get(uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) const; + Error SetOrAdd(SetAddMode aMode, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); + Error Delete(uint16_t aKey, int aIndex); + void Wipe(void); + + struct IndexMatcher + { + IndexMatcher(int aIndex) { mIndex = aIndex; } + + int mIndex; + }; + + struct Entry : public Heap::Allocatable, public LinkedListEntry + { + struct Value : public Heap::Allocatable, public LinkedListEntry + { + bool Matches(const IndexMatcher &aIndexMataher) const { return (AsNonConst(aIndexMataher).mIndex-- == 0); } + + Value *mNext; + Heap::Data mData; + }; + + bool Matches(uint16_t aKey) const { return mKey == aKey; } + + Entry *mNext; + uint16_t mKey; + OwningList mValues; + }; + + OwningList mEntries; +}; + +} // namespace Nexus +} // namespace ot + +#endif // OT_NEXUS_SETTINGS_HPP_ diff --git a/tests/nexus/platform/nexus_utils.hpp b/tests/nexus/platform/nexus_utils.hpp new file mode 100644 index 00000000000..8ec13d55d26 --- /dev/null +++ b/tests/nexus/platform/nexus_utils.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OT_NEXUS_UTIL_HPP_ +#define OT_NEXUS_UTIL_HPP_ + +#include +#include + +#include "common/arg_macros.hpp" + +/** + * Verifies a given error status to be successful (compared against value zero (0)), otherwise, it emits a + * given error messages and exits the program. + * + * @param[in] aStatus A scalar error status to be evaluated against zero (0). + * @param[in] aMessage An optional message (constant C string) to print on failure. + */ +#define SuccessOrQuit(...) \ + do \ + { \ + if ((OT_FIRST_ARG(__VA_ARGS__)) != 0) \ + { \ + fprintf(stderr, "\nFAILED %s:%d - SuccessOrQuit(%s)" OT_SECOND_ARG(__VA_ARGS__) "\n", __FUNCTION__, \ + __LINE__, _Stringize(OT_FIRST_ARG(__VA_ARGS__))); \ + exit(-1); \ + } \ + } while (false) + +/** + * Verifies that a given boolean condition is true, otherwise, it emits a given error message and exits the + * program. + * + * @param[in] aCondition A Boolean expression to be evaluated. + * @param[in] aMessage An optional message (constant C string) to print on failure. + */ +#define VerifyOrQuit(...) \ + do \ + { \ + if (!(OT_FIRST_ARG(__VA_ARGS__))) \ + { \ + fprintf(stderr, "\nFAILED %s:%d - VerifyOrQuit(%s) " OT_SECOND_ARG(__VA_ARGS__) "\n", __FUNCTION__, \ + __LINE__, _Stringize(OT_FIRST_ARG(__VA_ARGS__))); \ + exit(-1); \ + } \ + } while (false) + +// Private macros to convert `aArg` to string +#define _Stringize(aArg) _Stringize2(aArg) +#define _Stringize2(aArg) #aArg + +#endif // OT_NEXUS_UTIL_HPP_ diff --git a/tests/nexus/test_form_join.cpp b/tests/nexus/test_form_join.cpp new file mode 100644 index 00000000000..6973afd2465 --- /dev/null +++ b/tests/nexus/test_form_join.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "platform/nexus_core.hpp" +#include "platform/nexus_node.hpp" + +namespace ot { +namespace Nexus { + +void TestFormJoin(void) +{ + // Validate basic operations, forming a network and + // joining as router, FED, MED, and SED + + Core nexus; + + Node &leader = nexus.CreateNode(); + Node &fed = nexus.CreateNode(); + Node &sed = nexus.CreateNode(); + Node &med = nexus.CreateNode(); + Node &router1 = nexus.CreateNode(); + Node &router2 = nexus.CreateNode(); + + nexus.AdvanceTime(0); + + for (Node &node : nexus.GetNodes()) + { + node.GetInstance().SetLogLevel(kLogLevelInfo); + } + + Log("---------------------------------------------------------------------------------------"); + Log("Form network"); + + leader.Form(); + nexus.AdvanceTime(13 * 1000); + VerifyOrQuit(leader.Get().IsLeader()); + + Log("---------------------------------------------------------------------------------------"); + Log("Join an FED"); + + fed.Join(leader, Node::kAsFed); + nexus.AdvanceTime(2 * 1000); + VerifyOrQuit(fed.Get().IsChild()); + + Log("---------------------------------------------------------------------------------------"); + Log("Join an SED"); + + sed.Join(leader, Node::kAsSed); + nexus.AdvanceTime(2 * 1000); + VerifyOrQuit(sed.Get().IsChild()); + + Log("---------------------------------------------------------------------------------------"); + Log("Join an MED"); + + med.Join(leader, Node::kAsMed); + nexus.AdvanceTime(2 * 1000); + VerifyOrQuit(med.Get().IsChild()); + + Log("---------------------------------------------------------------------------------------"); + Log("Join two routers"); + + router1.Join(leader); + router2.Join(leader); + + Log("---------------------------------------------------------------------------------------"); + Log("Check all nodes roles and device modes"); + + nexus.AdvanceTime(300 * 1000); + + VerifyOrQuit(leader.Get().IsLeader()); + VerifyOrQuit(fed.Get().IsChild()); + VerifyOrQuit(sed.Get().IsChild()); + VerifyOrQuit(router1.Get().IsRouter()); + VerifyOrQuit(router2.Get().IsRouter()); + + VerifyOrQuit(fed.Get().IsRxOnWhenIdle()); + VerifyOrQuit(fed.Get().IsFullThreadDevice()); + + VerifyOrQuit(med.Get().IsRxOnWhenIdle()); + VerifyOrQuit(!med.Get().IsFullThreadDevice()); + VerifyOrQuit(med.Get().IsMinimalEndDevice()); + + VerifyOrQuit(!sed.Get().IsRxOnWhenIdle()); + VerifyOrQuit(!sed.Get().IsFullThreadDevice()); +} + +} // namespace Nexus +} // namespace ot + +int main(void) +{ + ot::Nexus::TestFormJoin(); + printf("All tests passed\n"); + return 0; +} diff --git a/tests/nexus/test_large_network.cpp b/tests/nexus/test_large_network.cpp new file mode 100644 index 00000000000..9e0d62ef1a3 --- /dev/null +++ b/tests/nexus/test_large_network.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "platform/nexus_core.hpp" +#include "platform/nexus_node.hpp" + +namespace ot { +namespace Nexus { + +static constexpr uint16_t kNumberOfRoles = (Mle::kRoleLeader + 1); + +typedef uint16_t RoleStats[kNumberOfRoles]; + +static void CaculateRoleStats(Core &aNexus, RoleStats &aRoleStats) +{ + ClearAllBytes(aRoleStats); + + for (Node &node : aNexus.GetNodes()) + { + aRoleStats[node.Get().GetRole()]++; + } +} + +static bool CheckRoleStats(const RoleStats &aRoleStats) +{ + return (aRoleStats[Mle::kRoleLeader] == 1) && (aRoleStats[Mle::kRoleDetached] == 0) && + (aRoleStats[Mle::kRoleDisabled] == 0); +} + +void Test(void) +{ + static constexpr uint16_t kNumNodes = 200; + + // All times in msec + static constexpr uint32_t kMaxWaitTime = 20 * Time::kOneMinuteInMsec; + static constexpr uint32_t kStatCollectionInterval = 125; + static constexpr uint32_t kExtraSimulTimAfterPass = 5 * Time::kOneSecondInMsec; + + Core nexus; + Node *leader; + RoleStats roleStats; + + for (uint16_t i = 0; i < kNumNodes; i++) + { + nexus.CreateNode(); + } + + nexus.AdvanceTime(0); + + Log("Starting %u nodes simultaneously", kNumNodes); + + leader = nexus.GetNodes().GetHead(); + leader->Form(); + + for (Node &node : nexus.GetNodes()) + { + if (&node == leader) + { + continue; + } + + node.Join(*leader); + } + + for (uint32_t step = 0; step < kMaxWaitTime / kStatCollectionInterval; step++) + { + if ((step % 20) == 0) + { + Log("+----------+----------+----------+----------+----------+"); + Log("| Leader | Router | Child | Detached | Disabled |"); + Log("+----------+----------+----------+----------+----------+"); + } + + CaculateRoleStats(nexus, roleStats); + + Log("| %8u | %8u | %8u | %8u | %8u |", roleStats[Mle::kRoleLeader], roleStats[Mle::kRoleRouter], + roleStats[Mle::kRoleChild], roleStats[Mle::kRoleDetached], roleStats[Mle::kRoleDisabled]); + + nexus.AdvanceTime(kStatCollectionInterval); + + if (CheckRoleStats(roleStats)) + { + break; + } + } + + VerifyOrQuit(CheckRoleStats(roleStats)); + + Log("========================================================="); + Log("All nodes are now part of the same partition"); + Log("Network stabilized after %u sec", nexus.GetNow().GetValue() / Time::kOneSecondInMsec); + Log("Continue simulation for another %u sec", kExtraSimulTimAfterPass / Time::kOneSecondInMsec); + Log("========================================================="); + + for (uint32_t step = 0; step < kExtraSimulTimAfterPass / kStatCollectionInterval; step++) + { + if ((step % 20) == 0) + { + Log("+----------+----------+----------+----------+----------+"); + Log("| Leader | Router | Child | Detached | Disabled |"); + Log("+----------+----------+----------+----------+----------+"); + } + + CaculateRoleStats(nexus, roleStats); + + Log("| %8u | %8u | %8u | %8u | %8u |", roleStats[Mle::kRoleLeader], roleStats[Mle::kRoleRouter], + roleStats[Mle::kRoleChild], roleStats[Mle::kRoleDetached], roleStats[Mle::kRoleDisabled]); + + nexus.AdvanceTime(kStatCollectionInterval); + } +} + +} // namespace Nexus +} // namespace ot + +int main(void) +{ + ot::Nexus::Test(); + printf("All tests passed\n"); + return 0; +}