From 6dc005fceac9273213b79522b94dcc13a96fbc6a Mon Sep 17 00:00:00 2001 From: David Rebbe Date: Tue, 14 Apr 2026 17:37:27 +0000 Subject: [PATCH] C2: Add PCBSN and MAC Address support --- api/icsneoc2/icsneoc2.cpp | 42 ++++++++ docs/icsneoc2/examples.rst | 8 ++ examples/CMakeLists.txt | 5 + examples/c2/device_info/CMakeLists.txt | 6 ++ examples/c2/device_info/src/main.c | 127 +++++++++++++++++++++++++ include/icsneo/icsneoc2.h | 22 +++++ test/unit/icsneoc2.cpp | 2 + 7 files changed, 212 insertions(+) create mode 100644 examples/c2/device_info/CMakeLists.txt create mode 100644 examples/c2/device_info/src/main.c diff --git a/api/icsneoc2/icsneoc2.cpp b/api/icsneoc2/icsneoc2.cpp index d73b8ca..56e7e58 100644 --- a/api/icsneoc2/icsneoc2.cpp +++ b/api/icsneoc2/icsneoc2.cpp @@ -381,6 +381,48 @@ icsneoc2_error_t icsneoc2_device_serial_get(const icsneoc2_device_t* device, cha return safe_str_copy(value, value_length, dev->getSerial()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; } +icsneoc2_error_t icsneoc2_device_pcb_serial_get(const icsneoc2_device_t* device, uint8_t* value, size_t* value_length) { + auto res = icsneoc2_device_is_valid(device); + if(res != icsneoc2_error_success) { + return res; + } + if(!value_length) { + return icsneoc2_error_invalid_parameters; + } + auto pcbSerial = device->device->getPCBSerial(); + if(!pcbSerial.has_value()) { + return icsneoc2_error_invalid_type; + } + const auto& data = *pcbSerial; + if(value) { + size_t copyLen = std::min(*value_length, data.size()); + std::copy(data.begin(), data.begin() + copyLen, value); + } + *value_length = data.size(); + return icsneoc2_error_success; +} + +icsneoc2_error_t icsneoc2_device_mac_address_get(const icsneoc2_device_t* device, uint8_t* value, size_t* value_length) { + auto res = icsneoc2_device_is_valid(device); + if(res != icsneoc2_error_success) { + return res; + } + if(!value_length) { + return icsneoc2_error_invalid_parameters; + } + auto macAddress = device->device->getMACAddress(); + if(!macAddress.has_value()) { + return icsneoc2_error_invalid_type; + } + const auto& data = *macAddress; + if(value) { + size_t copyLen = std::min(*value_length, data.size()); + std::copy(data.begin(), data.begin() + copyLen, value); + } + *value_length = data.size(); + return icsneoc2_error_success; +} + icsneoc2_error_t icsneoc2_device_go_online(const icsneoc2_device_t* device, bool go_online) { auto res = icsneoc2_device_is_valid(device); diff --git a/docs/icsneoc2/examples.rst b/docs/icsneoc2/examples.rst index f8b159b..377548d 100644 --- a/docs/icsneoc2/examples.rst +++ b/docs/icsneoc2/examples.rst @@ -25,3 +25,11 @@ Read Messages .. literalinclude:: ../../examples/c2/read_messages/src/main.c :language: c + +Device Info +=========== + +:download:`Download example <../../examples/c2/device_info/src/main.c>` + +.. literalinclude:: ../../examples/c2/device_info/src/main.c + :language: c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index bd5a733..b819cb9 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,6 +5,7 @@ option(LIBICSNEO_BUILD_C2_SIMPLE_EXAMPLE "Build the simple C2 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) +option(LIBICSNEO_BUILD_C2_DEVICE_INFO_EXAMPLE "Build the C2 device info example." ON) option(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE "Build the simple C++ example." ON) option(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE "Build the command-line interactive C++ example." ON) option(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE "Build the A2B example." ON) @@ -52,6 +53,10 @@ if(LIBICSNEO_BUILD_C2_RECONNECT_EXAMPLE) add_subdirectory(c2/reconnect) endif() +if(LIBICSNEO_BUILD_C2_DEVICE_INFO_EXAMPLE) + add_subdirectory(c2/device_info) +endif() + if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE) add_subdirectory(cpp/simple) endif() diff --git a/examples/c2/device_info/CMakeLists.txt b/examples/c2/device_info/CMakeLists.txt new file mode 100644 index 0000000..42a79b3 --- /dev/null +++ b/examples/c2/device_info/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(libicsneoc2-device-info-example src/main.c) +target_link_libraries(libicsneoc2-device-info-example icsneoc2-static) + +if(WIN32) + target_compile_definitions(libicsneoc2-device-info-example PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() diff --git a/examples/c2/device_info/src/main.c b/examples/c2/device_info/src/main.c new file mode 100644 index 0000000..02a645a --- /dev/null +++ b/examples/c2/device_info/src/main.c @@ -0,0 +1,127 @@ +#include + +#include +#include + +int print_error_code(const char* message, icsneoc2_error_t error) { + char error_str[64]; + 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 %d with error code %d\n", message, error, res); + return res; + } + printf("%s: \"%s\" (%u)\n", message, error_str, error); + return (int)error; +} + +int main() { + icsneoc2_error_t res; + + /* ===== Device Selection ===== */ + printf("Searching for devices...\n"); + icsneoc2_device_info_t* found_devices = NULL; + res = icsneoc2_device_enumerate(0, &found_devices); + if(res != icsneoc2_error_success) { + return print_error_code("Failed to enumerate devices", res); + } + if(!found_devices) { + printf("No devices found.\n"); + return 1; + } + + /* Count and display devices */ + int device_count = 0; + for(icsneoc2_device_info_t* cur = found_devices; cur; cur = icsneoc2_device_info_next(cur)) { + char desc[128] = {0}; + size_t desc_len = sizeof(desc); + icsneoc2_device_info_description_get(cur, desc, &desc_len); + + char serial[32] = {0}; + size_t serial_len = sizeof(serial); + icsneoc2_device_info_serial_get(cur, serial, &serial_len); + + printf(" [%d] %s (Serial: %s)\n", device_count + 1, desc, serial); + device_count++; + } + + int device_choice; + printf("Select device (1-%d): ", device_count); + if(scanf("%d", &device_choice) != 1 || device_choice < 1 || device_choice > device_count) { + printf("Invalid selection.\n"); + icsneoc2_enumeration_free(found_devices); + return 1; + } + + /* Find the selected device_info node */ + icsneoc2_device_info_t* selected_info = found_devices; + for(int i = 1; i < device_choice; i++) { + selected_info = icsneoc2_device_info_next(selected_info); + } + + /* Open the selected device */ + icsneoc2_device_t* device = NULL; + res = icsneoc2_device_create(selected_info, &device); + if(res != icsneoc2_error_success) { + icsneoc2_enumeration_free(found_devices); + return print_error_code("Failed to create device from device info", res); + } + res = icsneoc2_device_open(device, icsneoc2_open_options_default); + icsneoc2_enumeration_free(found_devices); + if(res != icsneoc2_error_success) { + icsneoc2_device_free(device); + return print_error_code("Failed to open device", res); + } + + char description[128] = {0}; + size_t description_length = sizeof(description); + icsneoc2_device_description_get(device, description, &description_length); + printf("\nOpened device: %s\n\n", description); + + /* ===== Serial Number ===== */ + char serial[32] = {0}; + size_t serial_len = sizeof(serial); + res = icsneoc2_device_serial_get(device, serial, &serial_len); + if(res == icsneoc2_error_success) { + printf("Serial: %s\n", serial); + } else { + print_error_code("Failed to get serial", res); + } + + /* ===== PCB Serial Number ===== */ + uint8_t pcbsn[16] = {0}; + size_t pcbsn_len = sizeof(pcbsn); + res = icsneoc2_device_pcb_serial_get(device, pcbsn, &pcbsn_len); + if(res == icsneoc2_error_success) { + printf("PCB Serial: "); + for(size_t i = 0; i < pcbsn_len; i++) { + printf("%c", pcbsn[i]); + } + printf("\n"); + } else { + print_error_code("Failed to get PCB serial (device may not support it)", res); + } + + /* ===== MAC Address ===== */ + uint8_t mac[6] = {0}; + size_t mac_len = sizeof(mac); + res = icsneoc2_device_mac_address_get(device, mac, &mac_len); + if(res == icsneoc2_error_success) { + printf("MAC: "); + for(size_t i = 0; i < mac_len; i++) { + if(i > 0) printf(":"); + printf("%02X", mac[i]); + } + printf("\n"); + } else { + print_error_code("Failed to get MAC address (device may not support it)", res); + } + + /* Cleanup */ + printf("\nClosing device... "); + res = icsneoc2_device_close(device); + printf("%s\n", res == icsneoc2_error_success ? "OK" : "FAIL"); + + icsneoc2_device_free(device); + return 0; +} diff --git a/include/icsneo/icsneoc2.h b/include/icsneo/icsneoc2.h index 61476e6..0cc4cce 100644 --- a/include/icsneo/icsneoc2.h +++ b/include/icsneo/icsneoc2.h @@ -290,6 +290,28 @@ icsneoc2_error_t icsneoc2_device_type_get(const icsneoc2_device_t* device, icsne */ icsneoc2_error_t icsneoc2_device_serial_get(const icsneoc2_device_t* device, char* value, size_t* value_length); +/** + * Get the PCB serial of a device. + * + * @param[in] device The device to get the PCB serial of. + * @param[out] value Pointer to a buffer to copy the PCB serial into. If NULL, only value_length is written. + * @param[in,out] value_length Size of the value buffer in bytes. Modified with the length of the PCB serial. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_type if the device does not have a PCB serial. + */ +icsneoc2_error_t icsneoc2_device_pcb_serial_get(const icsneoc2_device_t* device, uint8_t* value, size_t* value_length); + +/** + * Get the MAC address of a device. + * + * @param[in] device The device to get the MAC address of. + * @param[out] value Pointer to a buffer to copy the MAC address into. If NULL, only value_length is written. + * @param[in,out] value_length Size of the value buffer in bytes. Modified with the length of the MAC address. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_type if the device does not have a MAC address. + */ +icsneoc2_error_t icsneoc2_device_mac_address_get(const icsneoc2_device_t* device, uint8_t* value, size_t* value_length); + /** * Set the online state of a device. * diff --git a/test/unit/icsneoc2.cpp b/test/unit/icsneoc2.cpp index abc9295..f51b583 100644 --- a/test/unit/icsneoc2.cpp +++ b/test/unit/icsneoc2.cpp @@ -161,6 +161,8 @@ TEST(icsneoc2, test_icsneoc2_error_invalid_parameters_and_invalid_device) ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_rtc_get(NULL, (int64_t *)&placeholderUnsignedInteger64)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_rtc_set(NULL, 0)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_serial_get(NULL, placeholderStr, &placeholderSizeT)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_pcb_serial_get(NULL, &placeholderInteger8, &placeholderSizeT)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_mac_address_get(NULL, &placeholderInteger8, &placeholderSizeT)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_supports_tc10(NULL, &placeholderBool)); ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_timestamp_resolution_get(NULL, &placeholderInteger32));