From 60918c4b6de9ef8837dee91eae7acfc2cb85336a Mon Sep 17 00:00:00 2001 From: David Rebbe Date: Wed, 24 Jun 2026 19:16:51 +0000 Subject: [PATCH] C2: Add PerfTest setting --- api/icsneoc2/icsneoc2settings.cpp | 30 ++++++ docs/icsneoc2/examples.rst | 16 ++++ examples/CMakeLists.txt | 5 + examples/c2/perf_test/CMakeLists.txt | 6 ++ examples/c2/perf_test/src/main.c | 137 +++++++++++++++++++++++++++ include/icsneo/icsneoc2settings.h | 20 ++++ test/unit/icsneoc2.cpp | 2 + 7 files changed, 216 insertions(+) create mode 100644 examples/c2/perf_test/CMakeLists.txt create mode 100644 examples/c2/perf_test/src/main.c diff --git a/api/icsneoc2/icsneoc2settings.cpp b/api/icsneoc2/icsneoc2settings.cpp index 1fe68bb..ce5bf18 100644 --- a/api/icsneoc2/icsneoc2settings.cpp +++ b/api/icsneoc2/icsneoc2settings.cpp @@ -923,6 +923,36 @@ icsneoc2_error_t icsneoc2_settings_external_wifi_antenna_enabled_set(icsneoc2_de return icsneoc2_error_success; } +icsneoc2_error_t icsneoc2_settings_perf_test_enabled_get(icsneoc2_device_t* device, bool* value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if(res != icsneoc2_error_success) { + return res; + } + if(!value) { + return icsneoc2_error_invalid_parameters; + } + if(auto result = device->device->settings->isPerfTestEnabled(); result.has_value()) { + *value = result.value(); + return icsneoc2_error_success; + } else { + *value = false; + return icsneoc2_error_get_settings_failure; + } +} + +icsneoc2_error_t icsneoc2_settings_perf_test_enabled_set(icsneoc2_device_t* device, bool value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if(res != icsneoc2_error_success) { + return res; + } + if(!device->device->settings->setPerfTestEnable(value)) { + return icsneoc2_error_set_settings_failure; + } + return icsneoc2_error_success; +} + icsneoc2_error_t icsneoc2_settings_linux_configuration_port_get(icsneoc2_device_t* device, icsneoc2_linux_configuration_port_t* value) { // Make sure the device is valid auto res = icsneoc2_device_is_valid(device); diff --git a/docs/icsneoc2/examples.rst b/docs/icsneoc2/examples.rst index 9714035..e2253ca 100644 --- a/docs/icsneoc2/examples.rst +++ b/docs/icsneoc2/examples.rst @@ -90,3 +90,19 @@ TC10 .. literalinclude:: ../../examples/c2/tc10/src/main.c :language: c +gPTP +==== + +:download:`Download example <../../examples/c2/gptp/src/main.c>` + +.. literalinclude:: ../../examples/c2/gptp/src/main.c + :language: c + +PerfTest +======== + +:download:`Download example <../../examples/c2/perf_test/src/main.c>` + +.. literalinclude:: ../../examples/c2/perf_test/src/main.c + :language: c + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8967bbf..cc66f47 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,6 +15,7 @@ option(LIBICSNEO_BUILD_C2_ETHERNET_RECEIVE_EXAMPLE "Build the C2 ethernet receiv option(LIBICSNEO_BUILD_C2_T1S_LOOPBACK_EXAMPLE "Build the C2 RAD-Comet3 T1S loopback example." ON) option(LIBICSNEO_BUILD_C2_TC10_EXAMPLE "Build the C2 TC10 example." ON) option(LIBICSNEO_BUILD_C2_GPTP_EXAMPLE "Build the C2 gPTP settings example." ON) +option(LIBICSNEO_BUILD_C2_PERF_TEST_EXAMPLE "Build the C2 PerfTest setting example." ON) option(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE "Build the simple C++ example." ON) option(LIBICSNEO_BUILD_CPP_DEVICE_INFO_EXAMPLE "Build the C++ device info example." ON) option(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE "Build the command-line interactive C++ example." ON) @@ -104,6 +105,10 @@ if(LIBICSNEO_BUILD_C2_GPTP_EXAMPLE) add_subdirectory(c2/gptp) endif() +if(LIBICSNEO_BUILD_C2_PERF_TEST_EXAMPLE) + add_subdirectory(c2/perf_test) +endif() + if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE) add_subdirectory(cpp/simple) endif() diff --git a/examples/c2/perf_test/CMakeLists.txt b/examples/c2/perf_test/CMakeLists.txt new file mode 100644 index 0000000..43849fc --- /dev/null +++ b/examples/c2/perf_test/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(libicsneoc2-perf_test-example src/main.c) +target_link_libraries(libicsneoc2-perf_test-example icsneoc2-static) + +if(WIN32) + target_compile_definitions(libicsneoc2-perf_test-example PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() diff --git a/examples/c2/perf_test/src/main.c b/examples/c2/perf_test/src/main.c new file mode 100644 index 0000000..67fb1e3 --- /dev/null +++ b/examples/c2/perf_test/src/main.c @@ -0,0 +1,137 @@ +/* + * PerfTest setting example. + * + * Opens the first available device, enables the device-global PerfTest mode + * via icsneoc2_settings_perf_test_enabled_set(), reads messages for a few + * seconds while PerfTest is active, then disables PerfTest again. Each step + * reads the value back with icsneoc2_settings_perf_test_enabled_get() to + * confirm the change took effect. + */ + +#include +#include +#include + +#include +#include +#include + +static int print_error_code(const char* message, icsneoc2_error_t error) { + char error_str[64] = {0}; + size_t error_str_len = sizeof(error_str); + icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len); + if(res != icsneoc2_error_success) { + fprintf(stderr, "%s: failed to get string for error code %u\n", message, error); + return (int)res; + } + fprintf(stderr, "%s: \"%s\" (%u)\n", message, error_str, error); + return (int)error; +} + +/* Sets PerfTest, applies it to the device, then reads it back to confirm. */ +static icsneoc2_error_t set_and_verify_perf_test(icsneoc2_device_t* device, bool enable) { + icsneoc2_error_t res = icsneoc2_settings_perf_test_enabled_set(device, enable); + if(res != icsneoc2_error_success) + return res; + + res = icsneoc2_settings_apply(device); + if(res != icsneoc2_error_success) + return res; + + /* Re-read settings from the device so the read-back reflects what was + * actually persisted, not just the local settings buffer. */ + res = icsneoc2_settings_refresh(device); + if(res != icsneoc2_error_success) + return res; + + bool value = !enable; + res = icsneoc2_settings_perf_test_enabled_get(device, &value); + if(res != icsneoc2_error_success) + return res; + + printf("\tPerfTest now reads back as: %s\n", value ? "enabled" : "disabled"); + if(value != enable) { + fprintf(stderr, "\tERROR: expected PerfTest %s but device reports %s\n", + enable ? "enabled" : "disabled", value ? "enabled" : "disabled"); + return icsneoc2_error_get_settings_failure; + } + return icsneoc2_error_success; +} + +/* Continuously drains and counts messages for the given wall-clock duration. */ +static icsneoc2_error_t read_messages_for(icsneoc2_device_t* device, unsigned seconds) { + size_t total = 0; + printf("\tReading messages for %u seconds...\n", seconds); + time_t start = time(NULL); + while((time_t)(time(NULL) - start) < (time_t)seconds) { + /* Short timeout so an idle bus still lets us re-check the clock, + * while a flooding bus is drained as fast as messages arrive. */ + icsneoc2_message_t* message = NULL; + icsneoc2_error_t res = icsneoc2_device_message_get(device, &message, 50); + if(res != icsneoc2_error_success) + return res; + if(message == NULL) + continue; + ++total; + icsneoc2_message_free(message); + } + printf("\tReceived %zu messages in %u seconds.\n", total, seconds); + return icsneoc2_error_success; +} + +int main(void) { + printf("Opening first available device...\n"); + icsneoc2_device_t* device = NULL; + icsneoc2_error_t res = icsneoc2_device_open_first(0, icsneoc2_open_options_default, &device); + if(res != icsneoc2_error_success) + return print_error_code("\tFailed to open first device", res); + + char description[255] = {0}; + size_t description_length = sizeof(description); + res = icsneoc2_device_description_get(device, description, &description_length); + if(res != icsneoc2_error_success) { + icsneoc2_device_free(device); + return print_error_code("\tFailed to get device description", res); + } + printf("\tOpened device: %s\n", description); + + /* Pull the current settings down from the device before reading/modifying them. */ + res = icsneoc2_settings_refresh(device); + if(res != icsneoc2_error_success) + goto cleanup; + + bool initial = false; + res = icsneoc2_settings_perf_test_enabled_get(device, &initial); + if(res != icsneoc2_error_success) { + print_error_code("\tFailed to read initial PerfTest state (device may not support it)", res); + goto cleanup; + } + printf("\tInitial PerfTest state: %s\n", initial ? "enabled" : "disabled"); + + printf("Enabling PerfTest...\n"); + res = set_and_verify_perf_test(device, true); + if(res != icsneoc2_error_success) { + print_error_code("\tFailed to enable PerfTest", res); + goto cleanup; + } + + res = read_messages_for(device, 3); + if(res != icsneoc2_error_success) { + print_error_code("\tFailed while reading messages", res); + goto cleanup; + } + + printf("Disabling PerfTest...\n"); + res = set_and_verify_perf_test(device, false); + if(res != icsneoc2_error_success) { + print_error_code("\tFailed to disable PerfTest", res); + goto cleanup; + } + + printf("PerfTest enable/read/disable cycle completed successfully.\n"); + +cleanup: + icsneoc2_device_close(device); + icsneoc2_device_free(device); + return (int)res; +} diff --git a/include/icsneo/icsneoc2settings.h b/include/icsneo/icsneoc2settings.h index 602492e..f10abd1 100644 --- a/include/icsneo/icsneoc2settings.h +++ b/include/icsneo/icsneoc2settings.h @@ -696,6 +696,26 @@ icsneoc2_error_t icsneoc2_settings_external_wifi_antenna_enabled_get(icsneoc2_de */ icsneoc2_error_t icsneoc2_settings_external_wifi_antenna_enabled_set(icsneoc2_device_t* device, bool value); +/** + * Get whether the performance/throughput test mode is enabled. + * + * @param[in] device The device to query. + * @param[out] value Pointer to a bool to copy the value into (true if PerfTest is enabled). + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_get_settings_failure otherwise. + */ +icsneoc2_error_t icsneoc2_settings_perf_test_enabled_get(icsneoc2_device_t* device, bool* value); + +/** + * Set whether the performance/throughput test mode is enabled. + * + * @param[in] device The device to configure. + * @param[in] value true to enable PerfTest, false to disable it. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_set_settings_failure otherwise. + */ +icsneoc2_error_t icsneoc2_settings_perf_test_enabled_set(icsneoc2_device_t* device, bool value); + /** * Get which Ethernet port(s) are reserved for the Linux configuration interface (Fire3 family devices). * diff --git a/test/unit/icsneoc2.cpp b/test/unit/icsneoc2.cpp index 4d5d20c..d9b40e1 100644 --- a/test/unit/icsneoc2.cpp +++ b/test/unit/icsneoc2.cpp @@ -303,6 +303,8 @@ TEST(icsneoc2, test_icsneoc2_error_invalid_parameters_and_invalid_device) ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_linux_boot_enabled_set(NULL, false)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_external_wifi_antenna_enabled_get(NULL, &placeholderBool)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_external_wifi_antenna_enabled_set(NULL, false)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_perf_test_enabled_get(NULL, &placeholderBool)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_perf_test_enabled_set(NULL, false)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_linux_configuration_port_get(NULL, &placeholderLinuxConfigPort)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_linux_configuration_port_set(NULL, placeholderLinuxConfigPort)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_settings_disabled_get(NULL, &placeholderBool));