diff --git a/api/icsneoc2/icsneoc2.cpp b/api/icsneoc2/icsneoc2.cpp index 244af8bb..66374d5b 100644 --- a/api/icsneoc2/icsneoc2.cpp +++ b/api/icsneoc2/icsneoc2.cpp @@ -84,6 +84,61 @@ icsneoc2_error_t icsneoc2_error_code_get(icsneoc2_error_t error_code, char* valu return safe_str_copy(value, value_length, error_strings[error_code]) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; } +icsneoc2_error_t icsneoc2_supported_devices_enumerate(icsneoc2_supported_device_t** supported_devices) { + if(!supported_devices) { + return icsneoc2_error_invalid_parameters; + } + + auto device_types = DeviceFinder::GetSupportedDevices(); + icsneoc2_supported_device_t* head = nullptr; + icsneoc2_supported_device_t* tail = nullptr; + for(auto& device_type : device_types) { + auto* node = new (std::nothrow) icsneoc2_supported_device_t; + if(!node) { + return icsneoc2_error_out_of_memory; + } + node->device_type = device_type; + node->next = nullptr; + if(!head) { + head = node; + } else { + tail->next = node; + } + tail = node; + } + + *supported_devices = head; + return icsneoc2_error_success; +} + +icsneoc2_error_t icsneoc2_supported_devices_free(icsneoc2_supported_device_t* supported_devices) { + if(!supported_devices) { + return icsneoc2_error_invalid_parameters; + } + + while(supported_devices) { + auto* next = supported_devices->next; + delete supported_devices; + supported_devices = next; + } + return icsneoc2_error_success; +} + +icsneoc2_supported_device_t* icsneoc2_supported_devices_next(const icsneoc2_supported_device_t* supported_device) { + if(!supported_device) { + return nullptr; + } + return supported_device->next; +} + +icsneoc2_error_t icsneoc2_supported_device_get(const icsneoc2_supported_device_t* supported_device, icsneoc2_devicetype_t* device_type) { + if(!supported_device || !device_type) { + return icsneoc2_error_invalid_parameters; + } + *device_type = static_cast(supported_device->device_type); + return icsneoc2_error_success; +} + icsneoc2_error_t icsneoc2_device_type_name_get(icsneoc2_devicetype_t device_type, char* value, size_t* value_length) { if(!value || !value_length) { return icsneoc2_error_invalid_parameters; diff --git a/api/icsneoc2/icsneoc2_internal.h b/api/icsneoc2/icsneoc2_internal.h index 7bbfc6e2..5dca710c 100644 --- a/api/icsneoc2/icsneoc2_internal.h +++ b/api/icsneoc2/icsneoc2_internal.h @@ -13,6 +13,11 @@ using namespace icsneo; +typedef struct icsneoc2_supported_device_t { + DeviceType device_type; + icsneoc2_supported_device_t* next; +} icsneoc2_supported_device_t; + typedef struct icsneoc2_message_t { std::shared_ptr message; } icsneoc2_message_t; diff --git a/docs/icsneoc2/examples.rst b/docs/icsneoc2/examples.rst index 5cac86fd..97140350 100644 --- a/docs/icsneoc2/examples.rst +++ b/docs/icsneoc2/examples.rst @@ -10,6 +10,14 @@ Simple .. literalinclude:: ../../examples/c2/simple/src/main.c :language: c +Supported Devices +================= + +:download:`Download example <../../examples/c2/supported_devices/src/main.c>` + +.. literalinclude:: ../../examples/c2/supported_devices/src/main.c + :language: c + Disk Format =========== diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 359a8c8d..fa1ffd70 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -2,6 +2,7 @@ option(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE "Build the command-line interactive option(LIBICSNEO_BUILD_C_SIMPLE_EXAMPLE "Build the command-line simple C example." ON) option(LIBICSNEO_BUILD_C_LEGACY_EXAMPLE "Build the command-line simple C example." ON) option(LIBICSNEO_BUILD_C2_SIMPLE_EXAMPLE "Build the simple C2 example." ON) +option(LIBICSNEO_BUILD_C2_SUPPORTED_DEVICES_EXAMPLE "Build the C2 supported devices example." ON) option(LIBICSNEO_BUILD_C2_READ_MESSAGES_EXAMPLE "Build the C2 read messages example." ON) option(LIBICSNEO_BUILD_C2_DISKFORMAT_EXAMPLE "Build the C2 disk format example." ON) option(LIBICSNEO_BUILD_C2_RECONNECT_EXAMPLE "Build the C2 reconnect example." ON) @@ -48,6 +49,10 @@ if(LIBICSNEO_BUILD_C2_SIMPLE_EXAMPLE) add_subdirectory(c2/simple) endif() +if(LIBICSNEO_BUILD_C2_SUPPORTED_DEVICES_EXAMPLE) + add_subdirectory(c2/supported_devices) +endif() + if(LIBICSNEO_BUILD_C2_READ_MESSAGES_EXAMPLE) add_subdirectory(c2/read_messages) endif() diff --git a/examples/c2/supported_devices/CMakeLists.txt b/examples/c2/supported_devices/CMakeLists.txt new file mode 100644 index 00000000..db3a1734 --- /dev/null +++ b/examples/c2/supported_devices/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(libicsneoc2-supported-devices-example src/main.c) +target_link_libraries(libicsneoc2-supported-devices-example icsneoc2-static) + +if(WIN32) + target_compile_definitions(libicsneoc2-supported-devices-example PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() diff --git a/examples/c2/supported_devices/src/main.c b/examples/c2/supported_devices/src/main.c new file mode 100644 index 00000000..863084e4 --- /dev/null +++ b/examples/c2/supported_devices/src/main.c @@ -0,0 +1,55 @@ +#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) { + printf("%s: failed to get string for error code %u\n", message, error); + return (int)res; + } + printf("%s: %s (%u)\n", message, error_str, error); + return (int)error; +} + +int main(void) { + icsneoc2_supported_device_t* supported_devices = NULL; + icsneoc2_error_t res = icsneoc2_supported_devices_enumerate(&supported_devices); + if(res != icsneoc2_error_success) { + return print_error_code("Failed to enumerate supported devices", res); + } + + if(!supported_devices) { + printf("No supported devices found.\n"); + return 0; + } + + printf("Supported devices:\n"); + for(icsneoc2_supported_device_t* cur = supported_devices; cur != NULL; cur = icsneoc2_supported_devices_next(cur)) { + icsneoc2_devicetype_t device_type = 0; + res = icsneoc2_supported_device_get(cur, &device_type); + if(res != icsneoc2_error_success) { + icsneoc2_supported_devices_free(supported_devices); + return print_error_code("Failed to get supported device type", res); + } + + char name[128] = {0}; + size_t name_len = sizeof(name); + res = icsneoc2_device_type_name_get(device_type, name, &name_len); + if(res != icsneoc2_error_success) { + icsneoc2_supported_devices_free(supported_devices); + return print_error_code("Failed to get supported device type name", res); + } + + printf(" %s (%u)\n", name, (unsigned int)device_type); + } + + res = icsneoc2_supported_devices_free(supported_devices); + if(res != icsneoc2_error_success) { + return print_error_code("Failed to free supported device list", res); + } + + return 0; +} diff --git a/include/icsneo/icsneoc2.h b/include/icsneo/icsneoc2.h index 4dcb2c8d..e3827cca 100644 --- a/include/icsneo/icsneoc2.h +++ b/include/icsneo/icsneoc2.h @@ -19,6 +19,8 @@ typedef struct icsneoc2_message_t icsneoc2_message_t; typedef struct icsneoc2_event_t icsneoc2_event_t; +typedef struct icsneoc2_supported_device_t icsneoc2_supported_device_t; + typedef enum _icsneoc2_error_t { icsneoc2_error_success, // Function was successful icsneoc2_error_invalid_parameters, // Invalid parameters, typically because of a NULL reference. @@ -65,6 +67,44 @@ static const icsneoc2_open_options_t icsneoc2_open_options_default = */ icsneoc2_error_t icsneoc2_error_code_get(icsneoc2_error_t error_code, char* value, size_t* value_length); +/** + * Enumerate supported device types. + * + * @param[out] supported_devices Pointer to receive the head of the supported device types linked list. Must be freed with icsneoc2_supported_devices_free(). + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +icsneoc2_error_t icsneoc2_supported_devices_enumerate(icsneoc2_supported_device_t** supported_devices); + +/** + * Free a supported device types list returned by icsneoc2_supported_devices_enumerate(). + * + * @param[in] supported_devices The head of the supported device types linked list to free. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +icsneoc2_error_t icsneoc2_supported_devices_free(icsneoc2_supported_device_t* supported_devices); + +/** + * Advance to the next supported device type in a list. + * + * @param[in] supported_device The current supported device type node. + * + * @return The next supported device type node, or NULL at the end of the list. + */ + +icsneoc2_supported_device_t* icsneoc2_supported_devices_next(const icsneoc2_supported_device_t* supported_device); + +/** + * Get the device type from a supported device node. + * + * @param[in] supported_device The supported device node. + * @param[out] device_type Pointer to receive the device type. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +icsneoc2_error_t icsneoc2_supported_device_get(const icsneoc2_supported_device_t* supported_device, icsneoc2_devicetype_t* device_type); + /** * Get the device type string for a icsneoc2_devicetype_t. * diff --git a/test/unit/icsneoc2.cpp b/test/unit/icsneoc2.cpp index 39caa950..032b5fe2 100644 --- a/test/unit/icsneoc2.cpp +++ b/test/unit/icsneoc2.cpp @@ -3,6 +3,7 @@ #include #include #include "../../api/icsneoc2/icsneoc2_internal.h" +#include #include #include #include @@ -90,6 +91,34 @@ TEST(icsneoc2, test_icsneoc2_device_enumerate) icsneoc2_enumeration_free(devices); } +TEST(icsneoc2, test_icsneoc2_supported_devices_enumerate) +{ + icsneoc2_supported_device_t* supported_devices = nullptr; + ASSERT_EQ(icsneoc2_error_success, icsneoc2_supported_devices_enumerate(&supported_devices)); + ASSERT_NE(nullptr, supported_devices); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_supported_device_get(supported_devices, nullptr)); + + const auto& expected_devices = icsneo::DeviceFinder::GetSupportedDevices(); + size_t count = 0; + for(icsneoc2_supported_device_t* cur = supported_devices; cur != nullptr; cur = icsneoc2_supported_devices_next(cur)) { + ASSERT_LT(count, expected_devices.size()); + + icsneoc2_devicetype_t device_type = 0; + ASSERT_EQ(icsneoc2_error_success, icsneoc2_supported_device_get(cur, &device_type)); + ASSERT_EQ(static_cast(expected_devices[count].getDeviceType()), device_type); + + char name[ICSNEO_DEVICETYPE_LONGEST_NAME] = {0}; + size_t name_len = sizeof(name); + ASSERT_EQ(icsneoc2_error_success, icsneoc2_device_type_name_get(device_type, name, &name_len)); + ASSERT_STREQ(icsneo::DeviceType::GetGenericProductName(expected_devices[count].getDeviceType()), name); + + count++; + } + + ASSERT_EQ(expected_devices.size(), count); + ASSERT_EQ(icsneoc2_error_success, icsneoc2_supported_devices_free(supported_devices)); +} + TEST(icsneoc2, test_icsneoc2_device_is_valid) { ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_is_valid(NULL)); @@ -113,6 +142,10 @@ TEST(icsneoc2, test_icsneoc2_error_invalid_parameters_and_invalid_device) // All of these don't have a device parameter ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_enumerate(0, NULL)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_supported_devices_enumerate(NULL)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_supported_devices_free(NULL)); + ASSERT_EQ(nullptr, icsneoc2_supported_devices_next(NULL)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_supported_device_get(NULL, NULL)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_network_type_name_get(0, NULL, NULL)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_event_description_get(eventPlaceHolder, placeholderStr, &placeholderSizeT));