C2: Add icsneoc2_device_reconnect

master
David Rebbe 2026-04-08 20:19:31 +00:00 committed by Kyle Schwarz
parent 97a6b4a04f
commit 5b553b63d3
10 changed files with 331 additions and 57 deletions

View File

@ -43,7 +43,7 @@ bool safe_str_copy(char* dest, size_t* dest_size, std::string_view src) {
}
icsneoc2_error_t icsneoc2_error_code_get(icsneoc2_error_t error_code, char* value, size_t* value_length) {
static const char* error_strings[icsneoc2_error_maxsize] = {
static const char* error_strings[] = {
"Success", // icsneoc2_error_success
"Invalid function parameters", // icsneoc2_error_invalid_parameters
"Open failed", // icsneoc2_error_open_failed
@ -66,6 +66,8 @@ icsneoc2_error_t icsneoc2_error_code_get(icsneoc2_error_t error_code, char* valu
"Script clear failed", // icsneoc2_error_script_clear_failed
"Script upload failed", // icsneoc2_error_script_upload_failed
"Script load prepare failed", // icsneoc2_error_script_load_prepare_failed
"Close failed", // icsneoc2_error_close_failed
"Reconnect failed", // icsneoc2_error_reconnect_failed
};
static_assert(std::size(error_strings) == icsneoc2_error_maxsize,
"error_strings is out of sync with _icsneoc2_error_t enum - update both together");
@ -194,37 +196,27 @@ icsneoc2_error_t icsneoc2_device_is_open(const icsneoc2_device_t* device, bool*
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_device_is_disconnected(const icsneoc2_device_t* device, bool* is_disconnected) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!is_disconnected) {
icsneoc2_error_t icsneoc2_device_create(const icsneoc2_device_info_t* device_info, icsneoc2_device_t** device) {
if(!device_info || !device_info->device || !device) {
return icsneoc2_error_invalid_parameters;
}
auto dev = device->device;
*is_disconnected = dev->isDisconnected();
auto* new_device = new (std::nothrow) icsneoc2_device_t;
if(!new_device) {
return icsneoc2_error_out_of_memory;
}
new_device->device = device_info->device;
*device = new_device;
return icsneoc2_error_success;
}
static icsneoc2_error_t open_device_with_options(std::shared_ptr<Device> dev, icsneoc2_open_options_t options, icsneoc2_device_t** device) {
static icsneoc2_error_t open_device_with_options(std::shared_ptr<Device> dev, icsneoc2_open_options_t options) {
if(!dev) {
return icsneoc2_error_invalid_device;
}
// Nothing to do if already open
if(dev->isOpen()) {
*device = new (std::nothrow) icsneoc2_device_t;
if(!*device) {
return icsneoc2_error_out_of_memory;
}
(*device)->device = dev;
return icsneoc2_error_success;
}
if(!dev->enableMessagePolling(std::make_optional<MessageFilter>())) {
return icsneoc2_error_enable_message_polling_failed;
}
if(!dev->open()) {
if(!dev->isOpen() && !dev->open()) {
return icsneoc2_error_open_failed;
}
if((options & ICSNEOC2_OPEN_OPTIONS_SYNC_RTC) && !dev->setRTC(std::chrono::system_clock::now())) {
@ -235,23 +227,15 @@ static icsneoc2_error_t open_device_with_options(std::shared_ptr<Device> dev, ic
dev->close();
return icsneoc2_error_go_online_failed;
}
*device = new (std::nothrow) icsneoc2_device_t;
if(!*device) {
dev->close();
return icsneoc2_error_out_of_memory;
}
(*device)->device = dev;
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_device_open(const icsneoc2_device_info_t* device_info, icsneoc2_open_options_t options, icsneoc2_device_t** device) {
if(!device_info || !device) {
return icsneoc2_error_invalid_parameters;
icsneoc2_error_t icsneoc2_device_open(const icsneoc2_device_t* device, icsneoc2_open_options_t options) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!device_info->device) {
return icsneoc2_error_invalid_device;
}
return open_device_with_options(device_info->device, options, device);
return open_device_with_options(device->device, options);
}
icsneoc2_error_t icsneoc2_device_open_serial(const char* serial, icsneoc2_open_options_t options, icsneoc2_device_t** device) {
@ -266,7 +250,15 @@ icsneoc2_error_t icsneoc2_device_open_serial(const char* serial, icsneoc2_open_o
std::string_view target(serial);
for(auto* cur = devs; cur; cur = cur->next) {
if(cur->device && cur->device->getSerial() == target) {
res = open_device_with_options(cur->device, options, device);
if (res = icsneoc2_device_create(cur, device); res != icsneoc2_error_success) {
icsneoc2_enumeration_free(devs);
return res;
}
res = open_device_with_options((*device)->device, options);
if (res != icsneoc2_error_success) {
icsneoc2_device_free(*device);
*device = nullptr;
}
icsneoc2_enumeration_free(devs);
return res;
}
@ -286,7 +278,15 @@ icsneoc2_error_t icsneoc2_device_open_first(icsneoc2_devicetype_t device_type, i
}
for(auto* cur = devs; cur; cur = cur->next) {
if(cur->device && !cur->device->isOpen()) {
res = open_device_with_options(cur->device, options, device);
if (res = icsneoc2_device_create(cur, device); res != icsneoc2_error_success) {
icsneoc2_enumeration_free(devs);
return res;
}
res = open_device_with_options((*device)->device, options);
if (res != icsneoc2_error_success) {
icsneoc2_device_free(*device);
*device = nullptr;
}
icsneoc2_enumeration_free(devs);
return res;
}
@ -295,6 +295,34 @@ icsneoc2_error_t icsneoc2_device_open_first(icsneoc2_devicetype_t device_type, i
return icsneoc2_error_invalid_device;
}
icsneoc2_error_t icsneoc2_device_reconnect(icsneoc2_device_t* device, icsneoc2_open_options_t options, uint32_t timeout_ms) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
// If the device is currently open, close it first before trying to reconnect
if (device->device->isOpen()) {
res = icsneoc2_device_close(device);
if(res != icsneoc2_error_success) {
return res;
}
}
const auto timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
while(std::chrono::steady_clock::now() < timeout) {
icsneoc2_device_t* new_device = nullptr;
res = icsneoc2_device_open_serial(device->device->getSerial().c_str(), options, &new_device);
if(res == icsneoc2_error_success) {
device->device = new_device->device;
delete new_device;
return icsneoc2_error_success;
}
// Avoid busy looping
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return icsneoc2_error_reconnect_failed;
}
icsneoc2_error_t icsneoc2_device_close(icsneoc2_device_t* device) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
@ -304,7 +332,21 @@ icsneoc2_error_t icsneoc2_device_close(icsneoc2_device_t* device) {
if(!dev->isOpen()) {
return icsneoc2_error_success;
}
dev->close();
return dev->close() ? icsneoc2_error_success : icsneoc2_error_close_failed;
}
icsneoc2_error_t icsneoc2_device_free(icsneoc2_device_t* device) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if (device->device->isOpen()) {
res = icsneoc2_device_close(device);
if(res != icsneoc2_error_success) {
return res;
}
}
delete device;
return icsneoc2_error_success;
}

View File

@ -395,7 +395,7 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
if(heartbeatCV.wait_for(recvLk, std::chrono::milliseconds(3500), [&](){ return receivedMessage; })) {
receivedMessage = false;
} else {
if(!stopHeartbeatThread && !isDisconnected()) {
if(!stopHeartbeatThread) {
close();
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}

View File

@ -4,6 +4,7 @@ option(LIBICSNEO_BUILD_C_LEGACY_EXAMPLE "Build the command-line simple C example
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_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)
@ -47,6 +48,10 @@ if(LIBICSNEO_BUILD_C2_DISKFORMAT_EXAMPLE)
add_subdirectory(c2/diskformat)
endif()
if(LIBICSNEO_BUILD_C2_RECONNECT_EXAMPLE)
add_subdirectory(c2/reconnect)
endif()
if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE)
add_subdirectory(cpp/simple)
endif()

View File

@ -48,6 +48,7 @@ int main() {
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);
@ -58,11 +59,13 @@ int main() {
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to check disk formatting support", res);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return -1;
}
if(!supported) {
printf("\terror: %s does not support disk formatting\n", description);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return -1;
}
@ -79,6 +82,7 @@ int main() {
printf("FAIL\n");
print_error_code("\tFailed to get disk details", res);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return -1;
}
printf("OK\n");
@ -122,6 +126,7 @@ int main() {
printf("\n\terror: no disks are present in the device\n");
icsneoc2_disk_details_free(details);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return -1;
}
@ -133,6 +138,7 @@ int main() {
printf("\tAborted.\n");
icsneoc2_disk_details_free(details);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return 0;
}
@ -145,6 +151,7 @@ int main() {
print_error_code("\tFormat failed", res);
icsneoc2_disk_details_free(details);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return -1;
}
printf("\tFormat complete!\n");
@ -171,5 +178,6 @@ int main() {
printf("\tClosing device: %s...\n", description);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return 0;
}

View File

@ -83,6 +83,7 @@ int main() {
size_t description_length = 255;
res = icsneoc2_device_description_get(open_device, description, &description_length);
if(res != icsneoc2_error_success) {
icsneoc2_device_free(open_device);
return print_error_code("\tFailed to get device description", res);
};
printf("\tOpened device: %s\n", description);
@ -96,6 +97,7 @@ int main() {
res = icsneoc2_device_message_get(open_device, &messages[i], 0);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
return print_error_code("\tFailed to get messages from device", res);
};
if(messages[i] == NULL) {
@ -106,6 +108,7 @@ int main() {
}
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
return print_error_code("\tFailed to get messages from device", res);
}
time_t end_time = time(NULL);
@ -114,6 +117,7 @@ int main() {
res = process_message(messages, message_count);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
return print_error_code("\tFailed to process messages", res);
}
// Finally, close the device.
@ -121,8 +125,10 @@ int main() {
res = icsneoc2_device_close(open_device);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
return print_error_code("\tFailed to close device", res);
};
icsneoc2_device_free(open_device);
printf("\n");
return 0;

View File

@ -0,0 +1,6 @@
add_executable(libicsneoc2-reconnect-example src/main.c)
target_link_libraries(libicsneoc2-reconnect-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-reconnect-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()

View File

@ -0,0 +1,132 @@
#include <icsneo/icsneoc2.h>
#include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
void sleep_ms(uint32_t ms) {
#ifdef _WIN32
Sleep(ms);
#else
usleep(ms * 1000);
#endif
}
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;
}
void print_events(void) {
icsneoc2_event_t* events[256] = {0};
size_t events_count = 256;
for(size_t i = 0; i < events_count; ++i) {
icsneoc2_error_t res = icsneoc2_event_get(&events[i], NULL);
if(res != icsneoc2_error_success) {
(void)print_error_code("\tFailed to get events", res);
return;
}
if(events[i] == NULL) {
events_count = i;
break;
}
}
for(size_t i = 0; i < events_count; i++) {
char description[255] = {0};
size_t description_length = 255;
icsneoc2_error_t res = icsneoc2_event_description_get(events[i], description, &description_length);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to get event description", res);
continue;
}
printf("\tEvent %zu: %s\n", i, description);
}
for(size_t i = 0; i < events_count; i++) {
icsneoc2_event_free(events[i]);
}
if(events_count > 0) {
printf("\tReceived %zu events\n", events_count);
}
}
int main(void) {
/* Open the first available device */
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("Failed to open first device", res);
}
/* Get a description of the opened device */
char description[255] = {0};
size_t description_length = 255;
res = icsneoc2_device_description_get(device, description, &description_length);
if(res != icsneoc2_error_success) {
icsneoc2_device_free(device);
return print_error_code("Failed to get device description", res);
}
printf("Opened device: %s\n", description);
/* Wait for the device to disconnect */
printf("Waiting for device to disconnect (unplug device from PC)...\n");
for(;;) {
bool is_open = true;
res = icsneoc2_device_is_open(device, &is_open);
if(res != icsneoc2_error_success) {
print_events();
icsneoc2_device_free(device);
return print_error_code("Failed to check open status", res);
}
if(!is_open) {
printf("Device disconnected!\n");
break;
}
sleep_ms(500);
}
/* Attempt to reconnect */
uint32_t timeout_ms = 20000; // 20 second timeout
printf("Attempting to reconnect (%u second timeout)...\n", timeout_ms / 1000);
res = icsneoc2_device_reconnect(device, icsneoc2_open_options_default, timeout_ms);
if(res != icsneoc2_error_success) {
print_events();
icsneoc2_device_free(device);
return print_error_code("Failed to reconnect", res);
}
printf("Reconnected successfully!\n");
/* Verify by getting the description again */
description_length = 255;
res = icsneoc2_device_description_get(device, description, &description_length);
if(res == icsneoc2_error_success) {
printf("Device: %s\n", description);
}
/* Print any events */
print_events();
/* Close the device */
printf("Closing device...\n");
res = icsneoc2_device_close(device);
if(res != icsneoc2_error_success) {
icsneoc2_device_free(device);
return print_error_code("Failed to close device", res);
}
icsneoc2_device_free(device);
printf("Done.\n");
return 0;
}

View File

@ -116,6 +116,7 @@ int main() {
size_t description_length = 255;
res = icsneoc2_device_info_description_get(cur, description, &description_length);
if(res != icsneoc2_error_success) {
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to get device description", res);
};
printf("%.*s\n", (int)description_length, description);
@ -126,8 +127,15 @@ int main() {
printf("\tDevice open options: 0x%x\n", options);
printf("\tOpening device: %s...\n", description);
icsneoc2_device_t* open_device = NULL;
res = icsneoc2_device_open(cur, options, &open_device);
res = icsneoc2_device_create(cur, &open_device);
if(res != icsneoc2_error_success) {
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to create device", res);
}
res = icsneoc2_device_open(open_device, options);
if(res != icsneoc2_error_success) {
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to open device", res);
};
@ -137,6 +145,8 @@ int main() {
res = icsneoc2_device_timestamp_resolution_get(open_device, &timestamp_resolution);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to get timestamp resolution", res);
}
printf("%uns\n", timestamp_resolution);
@ -146,6 +156,8 @@ int main() {
res = icsneoc2_settings_baudrate_get(open_device, icsneoc2_netid_dwcan_01, &baudrate);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to get baudrate", res);
};
printf("%" PRIu64 "mbit/s\n", baudrate);
@ -155,6 +167,8 @@ int main() {
res = icsneoc2_settings_canfd_baudrate_get(open_device, icsneoc2_netid_dwcan_01, &fd_baudrate);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to get FD baudrate", res);
};
printf("%" PRIu64 "mbit/s\n", fd_baudrate);
@ -165,6 +179,8 @@ int main() {
res = icsneoc2_settings_baudrate_set(open_device, icsneoc2_netid_dwcan_01, baudrate);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to set baudrate", res);
};
printf("Ok\n");
@ -173,6 +189,8 @@ int main() {
res = icsneoc2_settings_canfd_baudrate_set(open_device, icsneoc2_netid_dwcan_01, fd_baudrate);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to set FD baudrate", res);
};
printf("Ok\n");
@ -181,6 +199,8 @@ int main() {
res = get_and_print_rtc(open_device);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to get RTC", res);
}
// Set RTC
@ -189,6 +209,8 @@ int main() {
res = icsneoc2_device_rtc_set(open_device, current_time);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to set RTC", res);
}
printf("Ok\n");
@ -197,6 +219,8 @@ int main() {
res = get_and_print_rtc(open_device);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to get RTC", res);
}
// Go online, start acking traffic
@ -204,6 +228,8 @@ int main() {
res = icsneoc2_device_go_online(open_device, true);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to go online", res);
}
// Redundant check to show how to check if the device is online, if the previous
@ -212,6 +238,8 @@ int main() {
res = icsneoc2_device_is_online(open_device, &is_online);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to check if online", res);
}
printf("%s\n", is_online ? "Online" : "Offline");
@ -219,6 +247,8 @@ int main() {
res = transmit_can_messages(open_device);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to transmit CAN messages", res);
}
// Wait for the bus to collect some messages, requires an active bus to get messages
@ -231,7 +261,12 @@ int main() {
for(size_t i = 0; i < message_count; ++i) {
res = icsneoc2_device_message_get(open_device, &messages[i], 0);
if(res != icsneoc2_error_success) {
for(size_t j = 0; j < i; ++j) {
icsneoc2_message_free(messages[j]);
}
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to get messages from device", res);
};
if(messages[i] == NULL) {
@ -243,7 +278,12 @@ int main() {
// Process the messages
res = process_messages(messages, message_count);
if(res != icsneoc2_error_success) {
for(size_t i = 0; i < message_count; ++i) {
icsneoc2_message_free(messages[i]);
}
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to process messages", res);
}
for(size_t i = 0; i < message_count; ++i) {
@ -254,11 +294,13 @@ int main() {
res = icsneoc2_device_close(open_device);
if(res != icsneoc2_error_success) {
print_events(description);
icsneoc2_device_free(open_device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("\tFailed to close device", res);
};
// Print device events
print_events(description);
icsneoc2_device_free(open_device);
}
icsneoc2_enumeration_free(found_devices);
printf("\n");
@ -285,6 +327,9 @@ void print_events(const char* device_description) {
// no device filter, get all events
icsneoc2_error_t res = icsneoc2_event_get(&events[i], NULL);
if(res != icsneoc2_error_success) {
for(size_t j = 0; j < i; ++j) {
icsneoc2_event_free(events[j]);
}
(void)print_error_code("\tFailed to get device events", res);
return;
}
@ -397,6 +442,7 @@ int transmit_can_messages(icsneoc2_device_t* device) {
res += icsneoc2_message_can_props_set(message, &arb_id, &flags);
res += icsneoc2_message_data_set(message, (uint8_t*)&counter, sizeof(counter));
if(res != icsneoc2_error_success) {
icsneoc2_message_free(message);
return print_error_code("\tFailed to modify message", res);
}
res = icsneoc2_device_message_transmit(device, message);

View File

@ -42,6 +42,8 @@ typedef enum _icsneoc2_error_t {
icsneoc2_error_script_clear_failed, // Failed to clear script
icsneoc2_error_script_upload_failed, // Failed to upload coremini script
icsneoc2_error_script_load_prepare_failed, // Failed to prepare script load
icsneoc2_error_close_failed, // Failed to close device
icsneoc2_error_reconnect_failed, // Failed to reconnect to device
// NOTE: Any new values added here should be updated in icsneoc2_error_code_get
icsneoc2_error_maxsize
} _icsneoc2_error_t;
@ -166,29 +168,31 @@ icsneoc2_error_t icsneoc2_device_is_valid(const icsneoc2_device_t* device);
icsneoc2_error_t icsneoc2_device_is_open(const icsneoc2_device_t* device, bool* is_open);
/**
* Check if a device is disconnected.
* Create a device handle from an enumeration node without opening it. Need to call icsneoc2_device_free() to free the handle when finished.
* The device can then be opened with icsneoc2_device_open().
*
* @param[in] device The device to check.
* @param[out] is_disconnected true if the device is disconnected, false otherwise
* @param[in] device_info The device info node to create from.
* @param[out] device Pointer to receive the created device handle.
*
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise.
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_invalid_device otherwise.
*
* @see icsneoc2_device_open icsneoc2_device_free
*/
icsneoc2_error_t icsneoc2_device_is_disconnected(const icsneoc2_device_t* device, bool* is_disconnected);
icsneoc2_error_t icsneoc2_device_create(const icsneoc2_device_info_t* device_info, icsneoc2_device_t** device);
/**
* Open a device from an enumeration node.
*
* After a successful call, icsneoc2_device_close() must be called to close the device.
*
* @param[in] device_info The device info node to open.
* @param[in] device Pointer to the device to open.
* @param[in] options Open options (e.g. icsneoc2_open_options_default).
* @param[out] device Pointer to receive the opened device handle.
*
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_open_failed otherwise.
*
* @see icsneoc2_device_close
* @see icsneoc2_device_close icsneoc2_device_free
*/
icsneoc2_error_t icsneoc2_device_open(const icsneoc2_device_info_t* device_info, icsneoc2_open_options_t options, icsneoc2_device_t** device);
icsneoc2_error_t icsneoc2_device_open(const icsneoc2_device_t* device, icsneoc2_open_options_t options);
/**
* Convenience: enumerate, find by serial, open, and free enumeration.
@ -199,7 +203,7 @@ icsneoc2_error_t icsneoc2_device_open(const icsneoc2_device_info_t* device_info,
*
* @return icsneoc2_error_t icsneoc2_error_success if successful.
*
* @see icsneoc2_device_close
* @see icsneoc2_device_close icsneoc2_device_free
*/
icsneoc2_error_t icsneoc2_device_open_serial(const char* serial, icsneoc2_open_options_t options, icsneoc2_device_t** device);
@ -207,30 +211,53 @@ icsneoc2_error_t icsneoc2_device_open_serial(const char* serial, icsneoc2_open_o
* Convenience: enumerate, find first available device (optionally filtered by type), open, and free enumeration.
* Pass 0 for device_type to match any device.
*
*
* @param[in] device_type The device type to match, or 0 for any.
* @param[in] options Open options (e.g. icsneoc2_open_options_default).
* @param[out] device Pointer to receive the opened device handle.
*
* @return icsneoc2_error_t icsneoc2_error_success if successful.
*
* @see icsneoc2_device_close
* @see icsneoc2_device_close icsneoc2_device_free
*/
icsneoc2_error_t icsneoc2_device_open_first(icsneoc2_devicetype_t device_type, icsneoc2_open_options_t options, icsneoc2_device_t** device);
/**
* Reconnect to a device. This is useful if the device was disconnected and reconnected, or if the connection was lost for some reason.
*
* @param[in] device The device to reconnect.
* @param[in] options Open options (e.g. icsneoc2_open_options_default).
* @param[in] timeout_ms The timeout in milliseconds to keep trying to reconnect before giving up.
*
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_reconnect_failed if the timeout was reached without reconnecting, or icsneoc2_device_is_valid() errors otherwise.
*/
icsneoc2_error_t icsneoc2_device_reconnect(icsneoc2_device_t* device, icsneoc2_open_options_t options, uint32_t timeout_ms);
/**
* Close a connection to a previously opened device.
*
* After a successful call to icsneoc2_device_open(), this function must be called to close the device.
* An already closed device will still succeed. All messages and events related to the device will be freed.
* An already closed device will still succeed. The device handle must be freed with icsneoc2_device_free() when finished.
*
* @param[in,out] device Pointer to the device to close.
*
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_device_is_valid() errors otherwise.
*
* @see icsneoc2_device_open icsneoc2_device_is_valid
* @see icsneoc2_device_open icsneoc2_device_is_valid icsneoc2_device_free
*/
icsneoc2_error_t icsneoc2_device_close(icsneoc2_device_t* device);
/**
* Free a device handle created by icsneoc2_device_create(). Device should be closed before freeing.
*
* @param[in] device The device handle to free.
*
* @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_device_is_valid() errors otherwise.
*
* @see icsneoc2_device_create icsneoc2_device_close
*/
icsneoc2_error_t icsneoc2_device_free(icsneoc2_device_t* device);
/**
* Get the description of a device
*

View File

@ -143,7 +143,10 @@ TEST(icsneoc2, test_icsneoc2_error_invalid_parameters_and_invalid_device)
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_info_description_get(NULL, placeholderStr, &placeholderSizeT));
// Test open/close with NULL parameters
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_open(NULL, 0, NULL));
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_create(NULL, NULL));
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_free(NULL));
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_open(NULL, 0));
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_open_serial(NULL, 0, NULL));
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_open_first(0, 0, NULL));
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_close(NULL));
@ -154,7 +157,6 @@ TEST(icsneoc2, test_icsneoc2_error_invalid_parameters_and_invalid_device)
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_is_online_supported(NULL, &placeholderBool));
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_is_valid(NULL));
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_is_open(NULL, &placeholderBool));
ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_is_disconnected(NULL, &placeholderBool));
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));