diff --git a/CMakeLists.txt b/CMakeLists.txt index 7732b07..42c27e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,8 @@ option(LIBICSNEO_BUILD_DOCS "Build documentation. Don't use in Visual Studio." O option(LIBICSNEO_BUILD_EXAMPLES "Build examples." ON) option(LIBICSNEO_BUILD_ICSNEOC "Build dynamic C library" ON) option(LIBICSNEO_BUILD_ICSNEOC_STATIC "Build static C library" ON) +option(LIBICSNEO_BUILD_ICSNEOC2 "Build dynamic C2 library" ON) +option(LIBICSNEO_BUILD_ICSNEOC2_STATIC "Build static C2 library" ON) option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON) option(LIBICSNEO_BUILD_ICSNEOLEGACY_STATIC "Build static icsnVC40 compatibility library" ON) set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to build with Npcap") @@ -28,6 +30,7 @@ option(LIBICSNEO_ENABLE_TCP "Enable devices which communicate over TCP" OFF) option(LIBICSNEO_ENABLE_FTD3XX "Enable devices which communicate over USB FTD3XX" ON) option(LIBICSNEO_ENABLE_BINDINGS_PYTHON "Enable Python library" OFF) +option(LIBICSNEO_ENABLE_BINDINGS_RUST "Enable Rust library" OFF) if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 17) @@ -334,6 +337,7 @@ endif() configure_file(api/icsneocpp/buildinfo.h.template ${CMAKE_CURRENT_BINARY_DIR}/generated/buildinfo.h) configure_file(api/icsneoc/version.rc.template ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc) +configure_file(api/icsneoc2/version.rc.template ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc2/version.rc) foreach(EXTINC ${LIBICSNEO_EXTENSION_INCLUDES}) message("Including " ${EXTINC}) @@ -461,6 +465,42 @@ if(LIBICSNEO_BUILD_ICSNEOC_STATIC) target_compile_definitions(icsneoc-static PUBLIC ICSNEOC_BUILD_STATIC) endif() +if(LIBICSNEO_BUILD_ICSNEOC2) + add_library(icsneoc2 SHARED api/icsneoc2/icsneoc2.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc2/version.rc) + target_include_directories(icsneoc2 + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + target_link_libraries(icsneoc2 PRIVATE icsneocpp) + target_compile_definitions(icsneoc2 + PRIVATE + ICSNEO_EXPORTS _CRT_SECURE_NO_WARNINGS + PUBLIC + ICSNEO_IMPORTS _CRT_SECURE_NO_WARNINGS + INTERFACE + ICSNEO_IMPORTS _CRT_SECURE_NO_WARNINGS + ) + target_compile_features(icsneoc2 PRIVATE cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums) + target_compile_definitions(icsneoc2 PUBLIC ICSNEOC2_BUILD_DYNAMIC) +endif() + +if(LIBICSNEO_BUILD_ICSNEOC2_STATIC) + add_library(icsneoc2-static STATIC api/icsneoc2/icsneoc2.cpp) + target_include_directories(icsneoc2-static + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + target_link_libraries(icsneoc2-static PUBLIC icsneocpp) + target_compile_features(icsneoc2-static PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums) + target_compile_definitions(icsneoc2-static PUBLIC ICSNEOC2_BUILD_STATIC) +endif() + if(LIBICSNEO_BUILD_ICSNEOLEGACY) add_library(icsneolegacy SHARED api/icsneolegacy/icsneolegacy.cpp @@ -527,10 +567,12 @@ if(LIBICSNEO_BUILD_UNIT_TESTS) test/unit/livedataencoderdecodertest.cpp test/unit/ringbuffertest.cpp test/unit/apperrordecodertest.cpp + test/unit/icsneoc2.cpp ) target_link_libraries(libicsneo-unit-tests gtest gtest_main) target_link_libraries(libicsneo-unit-tests icsneocpp) + target_link_libraries(libicsneo-unit-tests icsneoc2-static) target_include_directories(libicsneo-unit-tests PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) diff --git a/api/icsneo/icsneo.cpp b/api/icsneo/icsneo.cpp new file mode 100644 index 0000000..54fecc5 --- /dev/null +++ b/api/icsneo/icsneo.cpp @@ -0,0 +1,54 @@ +#include "icsneo/icsneo.h" +#include "icsneo/icsneocpp.h" +#include "icsneo/device/devicefinder.h" + +#include +#include + +using namespace icsneo; + +struct icsneoOpenOptions_t { + /** Open the device in online mode on open. */ + bool goOnlineOnOpen = true; + /** Enable message polling on open. */ + bool enablePollingOnOpen = true; + /** Synchronize the device RTC on open. + * Useful for devices that don't have an RTC battery like ValueCAN4 */ + bool syncRTCOnOpen = true; +}; + +typedef struct icsneo_device_t { + // This is the actual device handle, when its not open it will be NULL. + std::shared_ptr device; + + icsneoOpenOptions_t openOptions; +} *icsneo_device_ptr_t; + +icsneo_result_t icsneo_find(icsneo_device_ptr_t* devices, uint32_t* devices_count, void* reserved) { + static std::deque found_icsneo_devices; + if (!devices || !devices_count) { + return icsneo_error_invalid_parameter; + } + + // Find all devices and get the minimum size to process + auto found_devices = DeviceFinder::FindAll(); + auto min_size = std::minmax(found_devices.size(), static_cast(*devices_count)).first; + // lets resize the array to the minimum size + for (auto i = 0; i < min_size; i++) { + const auto* dev = devices[i]; + dev = new icsneo_device_t {}; + dev->device = found_devices[i]; + } + + + return icsneo_error_success; +} + +icsneo_result_t icsneo_open(icsneo_device_ptr_t* device) { + if (!device) { + return icsneo_error_invalid_parameter; + } + + + return icsneo_error_success; +} diff --git a/api/icsneo/version.rc.template b/api/icsneo/version.rc.template new file mode 100644 index 0000000..409bbed --- /dev/null +++ b/api/icsneo/version.rc.template @@ -0,0 +1,51 @@ +#define VER_FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@ +#define VER_FILEVERSION_STR "v@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@@BUILD_METADATA_PLUS@ @BUILD_GIT_INFO@" + +#define VER_PRODUCTVERSION VER_FILEVERSION +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR + +#ifndef DEBUG +#define VER_DEBUG 0 +#else +#define VER_DEBUG VS_FF_DEBUG +#endif + +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK (VS_FF_DEBUG) +FILEFLAGS (VER_DEBUG) +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", "Intrepid Control Systems, Inc." + VALUE "FileDescription", "Intrepid Control Systems Open Device Communication C API" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "icsneo.dll" + VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2024" + VALUE "OriginalFilename", "icsneo.dll" + VALUE "ProductName", "libicsneo" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + /* The following line should only be modified for localized versions. */ + /* It consists of any number of WORD,WORD pairs, with each pair */ + /* describing a language,codepage combination supported by the file. */ + /* */ + /* For example, a file might have values "0x409,1252" indicating that it */ + /* supports English language (0x409) in the Windows ANSI codepage (1252). */ + + VALUE "Translation", 0x409, 1252 + + END +END \ No newline at end of file diff --git a/api/icsneoc/icsneoc.cpp b/api/icsneoc/icsneoc.cpp index 91b4bdb..26df7e5 100644 --- a/api/icsneoc/icsneoc.cpp +++ b/api/icsneoc/icsneoc.cpp @@ -462,7 +462,7 @@ bool icsneo_transmit(const neodevice_t* device, const neomessage_t* message) { if(!icsneo_isValidNeoDevice(device)) return false; - if(auto frame = std::dynamic_pointer_cast(CreateMessageFromNeoMessage(message))) + if(auto frame = std::dynamic_pointer_cast(CreateMessageFromNeoMessage(message))) return device->device->transmit(frame); return false; @@ -738,7 +738,7 @@ int icsneo_getDeviceStatus(const neodevice_t* device, void* status, size_t* size if(!msg) // Did not receive a message return false; - auto rawMessage = std::static_pointer_cast(msg); + auto rawMessage = std::static_pointer_cast(msg); if(!rawMessage || (rawMessage->network.getNetID() != Network::NetID::DeviceStatus)) return false; diff --git a/api/icsneoc2/icsneoc2.cpp b/api/icsneoc2/icsneoc2.cpp new file mode 100644 index 0000000..12f56c6 --- /dev/null +++ b/api/icsneoc2/icsneoc2.cpp @@ -0,0 +1,1426 @@ +#include +#include +#include "icsneo/device/devicefinder.h" +#include "icsneo/icsneocpp.h" +#include "icsneo/communication/message/message.h" +#include "icsneo/communication/message/canmessage.h" +#include "icsneo/communication/message/linmessage.h" +#include "icsneo/communication/message/ethernetmessage.h" +#include "icsneo/communication/packet/canpacket.h" + +#include +#include +#include +#include + +using namespace icsneo; + +typedef struct icsneoc2_device_t { + std::shared_ptr device; + // Received messages from the device, we can automatically free them without the user. + std::vector> messages; + // Seperate buffer for transmit messages for simplicity. User is responsible for freeing. + // This needs to be a list so that pointers aren't invalidated on push_back or erase. + std::list> tx_messages; + std::vector events; + + icsneoc2_open_options_t options; + + icsneoc2_device_t(std::shared_ptr& device, icsneoc2_open_options_t options) : device(device), options(options) {} + + // Take care of cleaning up the device buffers like when closing the device + void clean_up() { + messages.clear(); + messages.shrink_to_fit(); + events.clear(); + events.shrink_to_fit(); + tx_messages.clear(); + } +} icsneoc2_device_t; + +// Any new members to this struct should be initialized in icsneoc2_device_messages_get() +typedef struct icsneoc2_message_t { + std::shared_ptr message; + // Indicates this message is a transmit message and should be in the tx_messages vector + bool is_tx; + + icsneoc2_message_t(std::shared_ptr& message, bool is_tx) : message(message), is_tx(is_tx) {} +} icsneoc2_message_t; + +typedef struct icsneoc2_event_t { + APIEvent event; +} icsneoc2_event_t; + + +static std::vector> g_devices; +static std::vector g_events; + +/** + * Safely copies a std::string to a char array. + * + * @param dest The buffer to copy the string into + * @param dest_size* The size of the buffer. Will be modified to the length of the string + * @param src The string to copy + * + * @return true if the string was successfully copied, false otherwise + * + * @note This function always null terminates the buffer, even if the string is too long. + * This is done for security reasons, not performance. + */ +bool safe_str_copy(const char* dest, uint32_t* const dest_size, std::string src) { + if (!dest || !dest_size) { + return false; + } + // zero terminate the entire string, this is done for security not for performance + memset(const_cast(dest), 0, *dest_size); + // Need to save room for the null terminator + *dest_size -= 1; + try { + auto copied = src.copy(const_cast(dest), static_cast(*dest_size), 0); + *dest_size = static_cast(copied); + // Somehow we didn't copy the number of bytes we needed to? This check probably isn't needed. + if (copied != src.length()) { + return false; + } + return true; + } catch (std::out_of_range& ex) { + // if pos > size() + (void)ex; + return false; + } +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_error_code_get(icsneoc2_error_t error_code, const char* value, uint32_t* value_length) { + if (!value || !value_length) { + return icsneoc2_error_invalid_parameters; + } + + std::string error("Unknown"); + switch (error_code) { + case icsneoc2_error_success: + error = "Success"; + break; + case icsneoc2_error_invalid_parameters: + error = "Invalid function parameters"; + break; + case icsneoc2_error_open_failed: + error = "Open failed"; + break; + case icsneoc2_error_go_online_failed: + error = "Going online failed"; + break; + case icsneoc2_error_enable_message_polling_failed: + error = "Enable message polling failed"; + break; + case icsneoc2_error_sync_rtc_failed: + error = "Syncronizing RTC failed"; + break; + case icsneoc2_error_get_messages_failed: + error = "Getting messages failed"; + break; + case icsneoc2_error_invalid_type: + error = "Invalid type"; + break; + case icsneoc2_error_rtc_failure: + error = "RTC failure"; + break; + case icsneoc2_error_set_settings_failure: + error = "Setting settings failed"; + break; + case icsneoc2_error_transmit_messages_failed: + error = "Transmitting messages failed"; + break; + case icsneoc2_error_string_copy_failed: + error = "String copy failed"; + break; + case icsneoc2_error_invalid_device: + error = "Invalid device"; + break; + case icsneoc2_error_invalid_message: + error = "Invalid message"; + break; + // Don't default, let the compiler warn us if we forget to handle an error code + } + // Copy the string into value + return safe_str_copy(value, value_length, error) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_type_name_get(icsneoc2_devicetype_t device_type, const char* value, uint32_t* value_length) { + if (!value || !value_length) { + return icsneoc2_error_invalid_parameters; + } + + auto device_type_str = DeviceType::GetGenericProductName(device_type); + // Copy the string into value + return safe_str_copy(value, value_length, device_type_str) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_find_all(icsneoc2_device_t** devices, uint32_t* devices_count, void* reserved) { + (void)reserved; + if (!devices ||!devices_count) { + return icsneoc2_error_invalid_parameters; + } + // Find devices + auto found_devices = DeviceFinder::FindAll(); + // Remove all devices that have been closed or are no longer connected + for (auto it = g_devices.begin(); it != g_devices.end();) { + if (!it->get()->device) { + it = g_devices.erase(it); + } else { + ++it; + } + } + // Add new devices + for (auto& found_device : found_devices) { + if (std::none_of(g_devices.begin(), g_devices.end(), + [&](const auto& device) { + return device->device == found_device; + })) { + auto default_options = icsneoc2_open_options_go_online | icsneoc2_open_options_enable_message_polling | icsneoc2_open_options_sync_rtc | icsneoc2_open_options_enable_auto_update; + auto device = std::make_shared(found_device, default_options); + g_devices.push_back(device); + } + } + // Determine how many we can return to the caller + auto min_size = std::minmax(static_cast(found_devices.size()), *devices_count).first; + *devices_count = min_size; + + // Return the devices to the caller + for (uint32_t i = 0; i < min_size; i++) { + devices[i] = g_devices[i].get(); + } + // Winner winner chicken dinner + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_is_valid(icsneoc2_device_t* device) { + if (!device) { + return icsneoc2_error_invalid_parameters; + } + + if (!std::any_of(g_devices.begin(), g_devices.end(), [&](const auto& dev) { + return dev.get() == device; + })) { + return icsneoc2_error_invalid_device; + } + if (!device->device) { + return icsneoc2_error_invalid_device; + } + + return !device->device ? icsneoc2_error_invalid_device : icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_is_open(icsneoc2_device_t* device, bool* is_open) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!is_open) { + return icsneoc2_error_invalid_parameters; + } + auto dev = device->device; + *is_open = dev->isOpen(); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_is_disconnected(icsneoc2_device_t* device, bool* is_disconnected) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!is_disconnected) { + return icsneoc2_error_invalid_parameters; + } + auto dev = device->device; + *is_disconnected = dev->isDisconnected(); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_open_options_get(icsneoc2_device_t* device, icsneoc2_open_options_t* options) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!options) { + return icsneoc2_error_invalid_parameters; + } + *options = device->options; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_open_options_set(icsneoc2_device_t* device, icsneoc2_open_options_t options) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + + device->options = options; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_open(icsneoc2_device_t* device) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Open the device + auto dev = device->device; + // Nothing to do if we are already open + if (dev->isOpen()) { + return icsneoc2_error_success; + } + // TODO: OpenFlags and OpenStatusHandler + // Open the device + if (!dev->open()) { + return icsneoc2_error_open_failed; + } + // Sync RTC + if ((device->options & icsneoc2_open_options_sync_rtc) == icsneoc2_open_options_sync_rtc && !dev->setRTC(std::chrono::system_clock::now())) { + dev->close(); + return icsneoc2_error_sync_rtc_failed; + } + // Enable message polling + if ((device->options & icsneoc2_open_options_enable_message_polling) == icsneoc2_open_options_enable_message_polling && !dev->enableMessagePolling()) { + dev->close(); + return icsneoc2_error_enable_message_polling_failed; + } + // Go online + if ((device->options & icsneoc2_open_options_go_online) == icsneoc2_open_options_go_online && !dev->goOnline()) { + dev->close(); + return icsneoc2_error_go_online_failed; + } + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_close(icsneoc2_device_t* device) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + auto dev = device->device; + if (!dev->isOpen()) { + return icsneoc2_error_success; + } + dev->close(); + // Clear out old messages and events + device->clean_up(); + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_description_get(icsneoc2_device_t* device, const char* value, uint32_t* value_length) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + auto dev = device->device; + // Copy the string into value + return safe_str_copy(value, value_length, dev->describe()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_type_get(icsneoc2_device_t* device, icsneoc2_devicetype_t* value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + auto dev = device->device; + *value = dev->getType().getDeviceType(); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_serial_get(icsneoc2_device_t* device, const char* value, uint32_t* value_length) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + auto dev = device->device; + // Copy the string into value + return safe_str_copy(value, value_length, dev->getSerial()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; +} + + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_go_online(icsneoc2_device_t* device, bool go_online) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + auto dev = device->device; + // Go online + if (go_online && dev->goOnline()) { + return icsneoc2_error_success; + } + // Go offline + if (!go_online && dev->goOffline()) { + return icsneoc2_error_success; + } + + return icsneoc2_error_go_online_failed; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_is_online(icsneoc2_device_t* device, bool* is_online) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!is_online) { + return icsneoc2_error_invalid_parameters; + } + auto dev = device->device; + *is_online = dev->isOnline(); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_is_online_supported(icsneoc2_device_t* device, bool* is_online_supported) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!is_online_supported) { + return icsneoc2_error_invalid_parameters; + } + auto dev = device->device; + *is_online_supported = dev->isOnlineSupported(); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_message_polling_set(icsneoc2_device_t* device, bool enable) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + auto dev = device->device; + // Enable message polling + if (enable && dev->enableMessagePolling()) { + return icsneoc2_error_success; + } + // Disable message polling + if (!enable && dev->disableMessagePolling()) { + return icsneoc2_error_success; + } + + return icsneoc2_error_enable_message_polling_failed; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_message_polling_get(icsneoc2_device_t* device, bool* is_enabled) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!is_enabled) { + return icsneoc2_error_invalid_parameters; + } + auto dev = device->device; + *is_enabled = dev->isMessagePollingEnabled(); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_message_polling_set_limit(icsneoc2_device_t* device, uint32_t limit) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + auto dev = device->device; + dev->setPollingMessageLimit(static_cast(limit)); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_message_polling_limit_get(icsneoc2_device_t* device, uint32_t* limit) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!limit) { + return icsneoc2_error_invalid_parameters; + } + auto dev = device->device; + *limit = static_cast(dev->getPollingMessageLimit()); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_message_count_get(icsneoc2_device_t* device, uint32_t* count) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!count) { + return icsneoc2_error_invalid_parameters; + } + auto dev = device->device; + *count = static_cast(dev->getCurrentMessageCount()); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_timestamp_resolution_get(icsneoc2_device_t* device, uint32_t* resolution) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!resolution) { + return icsneoc2_error_invalid_parameters; + } + auto dev = device->device; + *resolution = static_cast(dev->getTimestampResolution()); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_messages_get(icsneoc2_device_t* device, icsneoc2_message_t** messages, uint32_t* messages_count, uint32_t timeout_ms) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!messages || !messages_count) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the device is valid + res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + auto dev = device->device; + // Wait for messages + auto start_time = std::chrono::steady_clock::now(); + while (timeout_ms && std::chrono::duration_cast( + std::chrono::steady_clock::now() - start_time) + .count() < timeout_ms && + dev->getCurrentMessageCount() == 0) { + // Lets make sure we don't busy loop, we don't have any messages yet + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } + // Get the messages + auto results = dev->getMessages(); + auto& queried_messages = results.first; + auto& success = results.second; + if (!success) { + return icsneoc2_error_get_messages_failed; + } + // Find the minimum number of messages + uint32_t min_size = std::minmax(static_cast(queried_messages.size()), *messages_count).first; + *messages_count = min_size; + + // Copy the messages into our device message container + device->messages.clear(); + for (auto& message : queried_messages) { + auto message_t = std::make_shared(message, false); + device->messages.push_back(message_t); + } + device->messages.shrink_to_fit(); + // Copy the messages into the output array + for (uint32_t i = 0; i < min_size; i++) { + messages[i] = device->messages[i].get(); + } + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_messages_transmit(icsneoc2_device_t* device, icsneoc2_message_t** messages, uint32_t* messages_count) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!messages || !messages_count) { + return icsneoc2_error_invalid_parameters; + } + auto dev = device->device; + uint32_t i = 0; + bool success = false; + for (; i < *messages_count; i++) { + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, messages[i], &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + success = dev->transmit(std::static_pointer_cast(messages[i]->message)); + if (!success) { + break; + } + } + *messages_count = i; + + return success ? icsneoc2_error_success : icsneoc2_error_transmit_messages_failed; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_is_valid(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* is_valid) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!message || !is_valid) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the device is valid + res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // See if the message is a valid received message + bool is_rx_msg = std::any_of(device->messages.begin(), device->messages.end(), [&](const auto& msg) { + return msg.get() == message; + }); + // See if the message is a valid transmit message + bool is_tx_msg = std::any_of(device->tx_messages.begin(), device->tx_messages.end(), [&](const auto& msg) { + return msg.get() == message; + }); + + *is_valid = (is_rx_msg || is_tx_msg) && message->message.get() != nullptr; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_type_get(icsneoc2_device_t* device, icsneoc2_message_t* message, icsneoc2_msg_type_t* msg_type) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!message || !msg_type) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + + // Assign the message type + *msg_type = static_cast(message->message->getMsgType()); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_type_name_get(icsneoc2_msg_type_t msg_type, const char* value, uint32_t* value_length) { + if (!value || !value_length) { + return icsneoc2_error_invalid_parameters; + } + // Copy the string into value + return safe_str_copy(value, value_length, Message::getMsgTypeName(static_cast(msg_type))) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_bus_type_get(icsneoc2_device_t* device, icsneoc2_message_t* message, icsneoc2_msg_bus_type_t* bus_type) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!message || !bus_type) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + + // Make sure the message is a bus message + icsneoc2_msg_type_t msg_type = static_cast(message->message->getMsgType()); + if (msg_type != icsneoc2_msg_type_bus) { + return icsneoc2_error_invalid_type; + } + // We can static cast here because we are relying on the type being correct at this point + auto bus_message = static_cast(message->message.get()); + *bus_type = static_cast(bus_message->getBusType()); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_bus_type_name_get(icsneoc2_msg_bus_type_t bus_type, const char* value, uint32_t* value_length) { + if (!value || !value_length) { + return icsneoc2_error_invalid_parameters; + } + auto bus_type_str = std::string(Network::GetTypeString(static_cast(bus_type))); + // Copy the string into value + return safe_str_copy(value, value_length, bus_type_str) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_is_transmit(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!message || !value) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + + // Make sure the message is a bus message + icsneoc2_msg_type_t msg_type = static_cast(message->message->getMsgType()); + if (msg_type != icsneoc2_msg_type_bus) { + return icsneoc2_error_invalid_type; + } + // We can static cast here because we are relying on the type being correct at this point + auto bus_message = static_cast(message->message.get()); + *value = bus_message->transmitted; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_netid_get(icsneoc2_device_t* device, icsneoc2_message_t* message, icsneoc2_netid_t* netid) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!message || !netid) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + + // Make sure the message is a bus message + icsneoc2_msg_type_t msg_type = static_cast(message->message->getMsgType()); + if (msg_type != icsneoc2_msg_type_bus) { + return icsneoc2_error_invalid_type; + } + // We can static cast here because we are relying on the type being correct at this point + auto bus_message = static_cast(message->message.get()); + *netid = static_cast(bus_message->network.getNetID()); + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_netid_name_get(icsneoc2_netid_t netid, const char* value, uint32_t* value_length) { + if (!netid || !value || !value_length) { + return icsneoc2_error_invalid_parameters; + } + auto netid_str = std::string(Network::GetNetIDString(static_cast(netid), true)); + // Copy the string into value + return safe_str_copy(value, value_length, netid_str) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_netid_set(icsneoc2_device_t* device, icsneoc2_message_t* message, icsneoc2_netid_t netid) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + // Make sure the message has the data field, internal and bus currently have this. + icsneoc2_msg_type_t msg_type = static_cast(message->message->getMsgType()); + if (msg_type != icsneoc2_msg_type_internal && msg_type != icsneoc2_msg_type_bus) { + return icsneoc2_error_invalid_type; + } + auto* internal_message = dynamic_cast(message->message.get()); + if (!internal_message) { + return icsneoc2_error_invalid_type; + } + internal_message->network = Network(static_cast<_icsneoc2_netid_t>(netid), true); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_data_set(icsneoc2_device_t* device, icsneoc2_message_t* message, uint8_t* data, uint32_t data_length) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!message | !data) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + // Make sure the message has the data field, internal and bus currently have this. + icsneoc2_msg_type_t msg_type = static_cast(message->message->getMsgType()); + if (msg_type != icsneoc2_msg_type_internal && msg_type != icsneoc2_msg_type_bus) { + return icsneoc2_error_invalid_type; + } + auto* internal_message = dynamic_cast(message->message.get()); + if (!internal_message) { + return icsneoc2_error_invalid_type; + } + internal_message->data.clear(); + internal_message->data.resize(data_length); + internal_message->data.shrink_to_fit(); + std::copy(data, data + data_length, internal_message->data.begin()); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_data_get(icsneoc2_device_t* device, icsneoc2_message_t* message, uint8_t* data, uint32_t* data_length) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!message || !data || !data_length) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + // Make sure the message has the data field, internal and bus currently have this. + icsneoc2_msg_type_t msg_type = static_cast(message->message->getMsgType()); + if (msg_type != icsneoc2_msg_type_internal && msg_type != icsneoc2_msg_type_bus) { + return icsneoc2_error_invalid_type; + } + auto* data_message = dynamic_cast(message->message.get()); + if (!data_message) { + return icsneoc2_error_invalid_type; + } + auto min_length = std::minmax(static_cast(data_message->data.size()), *data_length).first; + *data_length = min_length; + std::copy(data_message->data.begin(), data_message->data.begin() + min_length, data); + + return icsneoc2_error_success; +} + + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_arbid_get(icsneoc2_device_t* device, icsneoc2_message_t* message, uint32_t* value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!message || !value) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + const auto* can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + *value = can_message->arbid; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_arbid_set(icsneoc2_device_t* device, icsneoc2_message_t* message, uint32_t value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!message) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + auto* const can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + can_message->arbid = value; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_dlc_get(icsneoc2_device_t* device, icsneoc2_message_t* message, int32_t* value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + if (!value) { + return icsneoc2_error_invalid_parameters; + } + const auto* can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + *value = static_cast(can_message->dlcOnWire); + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_dlc_set(icsneoc2_device_t* device, icsneoc2_message_t* message, int32_t value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + auto* const can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + if (value < 0) { + auto dlc_res = CAN_LengthToDLC(static_cast(can_message->data.size()), can_message->isCANFD); + can_message->dlcOnWire = dlc_res.value_or(0); + return dlc_res.has_value() ? icsneoc2_error_success : icsneoc2_error_invalid_parameters; + } else { + can_message->dlcOnWire = static_cast(value); + return icsneoc2_error_success; + } +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_is_remote(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + if (!value) { + return icsneoc2_error_invalid_parameters; + } + const auto* can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + *value = can_message->isRemote; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_set_remote(icsneoc2_device_t* device, icsneoc2_message_t* message, bool value) { + if (!message) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + auto* const can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + can_message->isRemote = value; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_is_extended(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + if (!value) { + return icsneoc2_error_invalid_parameters; + } + const auto* can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + *value = can_message->isExtended; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_extended_set(icsneoc2_device_t* device, icsneoc2_message_t* message, bool value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + auto* const can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + can_message->isExtended = value; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_is_canfd(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + if (!value) { + return icsneoc2_error_invalid_parameters; + } + const auto* can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + *value = can_message->isCANFD; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_canfd_set(icsneoc2_device_t* device, icsneoc2_message_t* message, bool value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + auto* const can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + can_message->isCANFD = value; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_baudrate_switch_get(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!message || !value) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + const auto* can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + *value = can_message->baudrateSwitch; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_baudrate_switch_set(icsneoc2_device_t* device, icsneoc2_message_t* message, bool value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + auto* const can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + can_message->baudrateSwitch = value; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_error_state_indicator_get(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + if (!value) { + return icsneoc2_error_invalid_parameters; + } + const auto* can_message = dynamic_cast(message->message.get()); + if (!can_message) { + return icsneoc2_error_invalid_type; + } + + *value = can_message->errorStateIndicator; + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_create(icsneoc2_device_t* device, icsneoc2_message_t** messages, uint32_t messages_count) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!messages) { + return icsneoc2_error_invalid_parameters; + } + auto dev = device->device; + // Get the device messages + for (uint32_t i = 0; i < messages_count; i++) { + auto can_message = std::static_pointer_cast(std::make_shared()); + auto message = std::make_shared(can_message, true); + device->tx_messages.push_back(message); + messages[i] = message.get(); + } + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_free(icsneoc2_device_t* device, icsneoc2_message_t* message) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + // Make sure the message is valid + bool is_msg_valid = false; + res = icsneoc2_message_is_valid(device, message, &is_msg_valid); + if (res != icsneoc2_error_success) { + return res; + } else if (!is_msg_valid) { + return icsneoc2_error_invalid_message; + } + bool removed = false; + for (auto it = device->tx_messages.begin(); it != device->tx_messages.end(); it++) { + if (it->get() == message) { + device->tx_messages.erase(it); + removed = true; + message = nullptr; + break; + } + } + return removed ? icsneoc2_error_success : icsneoc2_error_invalid_parameters; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_events_get(icsneoc2_event_t** events, uint32_t* events_count) { + if (!events || !events_count) { + return icsneoc2_error_invalid_parameters; + } + // Clear the device events + g_events.clear(); + // Get the global events + auto global_events = EventManager::GetInstance().get(); + // Get the mininum number of events + auto min_size = std::minmax(static_cast(global_events.size()), *events_count).first; + *events_count = min_size; + // Copy the events into the global event container + for (uint32_t i = 0; i < min_size; i++) { + auto e = icsneoc2_event_t { + global_events[i], + }; + g_events.push_back(e); + } + g_events.shrink_to_fit(); + // Copy the global events references into the events array + for (uint32_t i = 0; i < min_size; i++) { + events[i] = &g_events[i]; + } + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_events_get(icsneoc2_device_t* device, icsneoc2_event_t** events, uint32_t* events_count) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!events || !events_count) { + return icsneoc2_error_invalid_parameters; + } + // Setup the event filter + EventFilter filter(device->device.get()); + // Clear the device events + device->events.clear(); + // Get the mininum number of events + auto min_size = std::minmax(static_cast(icsneo::EventCount(filter)), *events_count).first; + *events_count = min_size; + // GetEvents uses 0 as unlimited, where the API can't allocate anything. + if (min_size == 0) { + return icsneoc2_error_success; + } + // Copy the events into the device event container + auto device_events = icsneo::GetEvents(filter, min_size); + for (uint32_t i = 0; i < min_size; i++) { + auto e = icsneoc2_event_t { + device_events[i], + }; + device->events.push_back(e); + } + device->events.shrink_to_fit(); + // Copy the device events references into the events array + for (uint32_t i = 0; i < min_size; i++) { + events[i] = &device->events[i]; + } + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_event_description_get(icsneoc2_event_t* event, const char* value, uint32_t* value_length) { + if (!event || !value || !value_length) { + return icsneoc2_error_invalid_parameters; + } + // TODO: Check if event is valid + auto ev = event->event; + // Copy the string into value + return safe_str_copy(value, value_length, ev.describe()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_rtc_get(icsneoc2_device_t* device, int64_t* unix_epoch) { + if (!unix_epoch) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (auto rtc_time = device->device->getRTC(); rtc_time != std::nullopt) { + *unix_epoch = std::chrono::duration_cast(rtc_time->time_since_epoch()).count(); + } else { + *unix_epoch = 0; + return icsneoc2_error_rtc_failure; + } + return icsneoc2_error_success; +} +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_rtc_set(icsneoc2_device_t* device, int64_t unix_epoch) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!device->device->setRTC(std::chrono::system_clock::time_point(std::chrono::seconds(unix_epoch)))) { + return icsneoc2_error_sync_rtc_failed; + } + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_load_default_settings(icsneoc2_device_t* device, bool save) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!device->device->settings->applyDefaults(!save)) { + return icsneoc2_error_set_settings_failure; + } + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_baudrate_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint64_t* baudrate) { + if (!baudrate) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + *baudrate = device->device->settings->getBaudrateFor(Network(netid)); + if (*baudrate < 0) { + return icsneoc2_error_invalid_type; + } + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_baudrate_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint64_t baudrate, bool save) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!device->device->settings->setBaudrateFor(Network(netid), baudrate)) { + return icsneoc2_error_set_settings_failure; + } + if (save) { + if (!device->device->settings->apply()) { + return icsneoc2_error_set_settings_failure; + } + } + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_canfd_baudrate_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint64_t* baudrate) { + if (!baudrate) { + return icsneoc2_error_invalid_parameters; + } + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + *baudrate = device->device->settings->getFDBaudrateFor(Network(netid)); + if (*baudrate < 0) { + return icsneoc2_error_invalid_type; + } + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_canfd_baudrate_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint64_t baudrate, bool save) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + if (!device->device->settings->setFDBaudrateFor(Network(netid), baudrate)) { + return icsneoc2_error_set_settings_failure; + } + if (save) { + if (!device->device->settings->apply()) { + return icsneoc2_error_set_settings_failure; + } + } + + return icsneoc2_error_success; +} + +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_supports_tc10(icsneoc2_device_t* device, bool* supported) { + // Make sure the device is valid + auto res = icsneoc2_device_is_valid(device); + if (res != icsneoc2_error_success) { + return res; + } + *supported = device->device->supportsTC10(); + + return icsneoc2_error_success; +} diff --git a/api/icsneoc2/version.rc.template b/api/icsneoc2/version.rc.template new file mode 100644 index 0000000..409bbed --- /dev/null +++ b/api/icsneoc2/version.rc.template @@ -0,0 +1,51 @@ +#define VER_FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@ +#define VER_FILEVERSION_STR "v@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@@BUILD_METADATA_PLUS@ @BUILD_GIT_INFO@" + +#define VER_PRODUCTVERSION VER_FILEVERSION +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR + +#ifndef DEBUG +#define VER_DEBUG 0 +#else +#define VER_DEBUG VS_FF_DEBUG +#endif + +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK (VS_FF_DEBUG) +FILEFLAGS (VER_DEBUG) +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", "Intrepid Control Systems, Inc." + VALUE "FileDescription", "Intrepid Control Systems Open Device Communication C API" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "icsneo.dll" + VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2024" + VALUE "OriginalFilename", "icsneo.dll" + VALUE "ProductName", "libicsneo" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + /* The following line should only be modified for localized versions. */ + /* It consists of any number of WORD,WORD pairs, with each pair */ + /* describing a language,codepage combination supported by the file. */ + /* */ + /* For example, a file might have values "0x409,1252" indicating that it */ + /* supports English language (0x409) in the Windows ANSI codepage (1252). */ + + VALUE "Translation", 0x409, 1252 + + END +END \ No newline at end of file diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt index d243d3b..59075b8 100644 --- a/bindings/CMakeLists.txt +++ b/bindings/CMakeLists.txt @@ -1,3 +1,18 @@ if(LIBICSNEO_ENABLE_BINDINGS_PYTHON) add_subdirectory(python) +endif() + +if(LIBICSNEO_ENABLE_BINDINGS_RUST) + include(FetchContent) + + FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git + GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here + ) + FetchContent_MakeAvailable(Corrosion) + + corrosion_import_crate(MANIFEST_PATH ${CMAKE_SOURCE_DIR}/bindings/rust/icsneoc2rs/Cargo.toml) +else() + message(STATUS "Not building rust bindings") endif() \ No newline at end of file diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 22fcfd8..eab9a24 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -28,6 +28,7 @@ target_link_libraries(icsneopy PRIVATE icsneocpp) install(TARGETS icsneopy LIBRARY DESTINATION icsneopy) -add_custom_command(TARGET icsneopy POST_BUILD COMMAND stubgen -m icsneopy -o .) +find_program(STUBGEN stubgen REQUIRED) +add_custom_command(TARGET icsneopy POST_BUILD COMMAND ${STUBGEN} -m icsneopy -o .) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/icsneopy.pyi __init__.py py.typed DESTINATION icsneopy) diff --git a/bindings/python/icsneopy/communication/message/canmessage.cpp b/bindings/python/icsneopy/communication/message/canmessage.cpp index 68d36ed..1091a5a 100644 --- a/bindings/python/icsneopy/communication/message/canmessage.cpp +++ b/bindings/python/icsneopy/communication/message/canmessage.cpp @@ -7,7 +7,7 @@ namespace icsneo { void init_canmessage(pybind11::module_& m) { - pybind11::class_, Frame>(m, "CANMessage") + pybind11::class_, BusMessage>(m, "CANMessage") .def(pybind11::init()) .def_readwrite("arbid", &CANMessage::arbid) .def_readwrite("dlcOnWire", &CANMessage::dlcOnWire) diff --git a/bindings/python/icsneopy/communication/message/ethernetmessage.cpp b/bindings/python/icsneopy/communication/message/ethernetmessage.cpp index 5904309..8b77bef 100644 --- a/bindings/python/icsneopy/communication/message/ethernetmessage.cpp +++ b/bindings/python/icsneopy/communication/message/ethernetmessage.cpp @@ -11,7 +11,7 @@ void init_ethernetmessage(pybind11::module_& m) { .def("to_string", &MACAddress::toString) .def("__repr__", &MACAddress::toString); - pybind11::class_, Frame>(m, "EthernetMessage") + pybind11::class_, BusMessage>(m, "EthernetMessage") .def(pybind11::init()) .def_readwrite("preemptionEnabled", &EthernetMessage::preemptionEnabled) .def_readwrite("preemptionFlags", &EthernetMessage::preemptionFlags) diff --git a/bindings/python/icsneopy/communication/message/linmessage.cpp b/bindings/python/icsneopy/communication/message/linmessage.cpp index cfee887..9265234 100644 --- a/bindings/python/icsneopy/communication/message/linmessage.cpp +++ b/bindings/python/icsneopy/communication/message/linmessage.cpp @@ -30,7 +30,7 @@ void init_linmessage(pybind11::module_& m) { .def_readwrite("BusRecovered", &LINStatusFlags::BusRecovered) .def_readwrite("BreakOnly", &LINStatusFlags::BreakOnly); - pybind11::class_, Frame> linMessage(m, "LINMessage"); + pybind11::class_, BusMessage> linMessage(m, "LINMessage"); pybind11::enum_(linMessage, "Type") .value("NOT_SET", LINMessage::Type::NOT_SET) diff --git a/bindings/python/icsneopy/communication/message/mdiomessage.cpp b/bindings/python/icsneopy/communication/message/mdiomessage.cpp index 066ff5a..d8ac784 100644 --- a/bindings/python/icsneopy/communication/message/mdiomessage.cpp +++ b/bindings/python/icsneopy/communication/message/mdiomessage.cpp @@ -7,7 +7,7 @@ namespace icsneo { void init_mdiomessage(pybind11::module_& m) { - pybind11::class_, Frame> mdioMessage(m, "MDIOMessage"); + pybind11::class_, BusMessage> mdioMessage(m, "MDIOMessage"); pybind11::enum_(mdioMessage, "Clause") .value("Clause45", MDIOMessage::Clause::Clause45) .value("Clause22", MDIOMessage::Clause::Clause22); diff --git a/bindings/python/icsneopy/communication/message/message.cpp b/bindings/python/icsneopy/communication/message/message.cpp index 5fef94e..8b07c32 100644 --- a/bindings/python/icsneopy/communication/message/message.cpp +++ b/bindings/python/icsneopy/communication/message/message.cpp @@ -9,13 +9,13 @@ namespace icsneo { void init_message(pybind11::module_& m) { pybind11::class_> message(m, "Message"); pybind11::enum_(message, "Type") - .value("Frame", Message::Type::Frame) + .value("BusMessage", Message::Type::BusMessage) .value("CANErrorCount", Message::Type::CANErrorCount) .value("CANError", Message::Type::CANError) .value("LINHeaderOnly", Message::Type::LINHeaderOnly) .value("LINBreak", Message::Type::LINBreak) .value("Invalid", Message::Type::Invalid) - .value("RawMessage", Message::Type::RawMessage) + .value("InternalMessage", Message::Type::InternalMessage) .value("ReadSettings", Message::Type::ReadSettings) .value("ResetStatus", Message::Type::ResetStatus) .value("DeviceVersion", Message::Type::DeviceVersion) @@ -40,14 +40,14 @@ void init_message(pybind11::module_& m) { message.def_readonly("type", &Message::type); message.def_readwrite("timestamp", &Message::timestamp); - pybind11::class_, Message>(m, "RawMessage") - .def_readwrite("network", &RawMessage::network) - .def_readwrite("data", &RawMessage::data); + pybind11::class_, Message>(m, "InternalMessage") + .def_readwrite("network", &InternalMessage::network) + .def_readwrite("data", &InternalMessage::data); - pybind11::class_, RawMessage>(m, "Frame") - .def_readwrite("description", &Frame::description) - .def_readwrite("transmitted", &Frame::transmitted) - .def_readwrite("error", &Frame::error); + pybind11::class_, InternalMessage>(m, "BusMessage") + .def_readwrite("description", &BusMessage::description) + .def_readwrite("transmitted", &BusMessage::transmitted) + .def_readwrite("error", &BusMessage::error); } } // namespace icsneo diff --git a/bindings/python/icsneopy/device/device.cpp b/bindings/python/icsneopy/device/device.cpp index 08ab186..f33720f 100644 --- a/bindings/python/icsneopy/device/device.cpp +++ b/bindings/python/icsneopy/device/device.cpp @@ -28,7 +28,7 @@ void init_device(pybind11::module_& m) { .def("set_polling_message_limit", &Device::setPollingMessageLimit) .def("add_message_callback", &Device::addMessageCallback, pybind11::call_guard()) .def("remove_message_callback", &Device::removeMessageCallback, pybind11::call_guard()) - .def("transmit", pybind11::overload_cast>(&Device::transmit), pybind11::call_guard()) + .def("transmit", pybind11::overload_cast>(&Device::transmit), pybind11::call_guard()) .def("get_supported_rx_networks", &Device::getSupportedRXNetworks, pybind11::return_value_policy::reference) .def("get_supported_tx_networks", &Device::getSupportedTXNetworks, pybind11::return_value_policy::reference) .def("get_rtc", &Device::getRTC, pybind11::call_guard()) diff --git a/bindings/python/icsneopy/device/devicetype.cpp b/bindings/python/icsneopy/device/devicetype.cpp index 57a9bb7..0751546 100644 --- a/bindings/python/icsneopy/device/devicetype.cpp +++ b/bindings/python/icsneopy/device/devicetype.cpp @@ -48,14 +48,10 @@ void init_devicetype(pybind11::module_& m) { .value("OBD2_PRO", DeviceType::Enum::OBD2_PRO) .value("ECUChip_UART", DeviceType::Enum::ECUChip_UART) .value("PLASMA", DeviceType::Enum::PLASMA) - .value("DONT_REUSE0", DeviceType::Enum::DONT_REUSE0) .value("NEOAnalog", DeviceType::Enum::NEOAnalog) .value("CT_OBD", DeviceType::Enum::CT_OBD) - .value("DONT_REUSE1", DeviceType::Enum::DONT_REUSE1) - .value("DONT_REUSE2", DeviceType::Enum::DONT_REUSE2) .value("ION", DeviceType::Enum::ION) .value("RADStar", DeviceType::Enum::RADStar) - .value("DONT_REUSE3", DeviceType::Enum::DONT_REUSE3) .value("VCAN4_4", DeviceType::Enum::VCAN4_4) .value("VCAN4_2", DeviceType::Enum::VCAN4_2) .value("CMProbe", DeviceType::Enum::CMProbe) diff --git a/bindings/rust/icsneoc2rs/.gitignore b/bindings/rust/icsneoc2rs/.gitignore new file mode 100644 index 0000000..0104787 --- /dev/null +++ b/bindings/rust/icsneoc2rs/.gitignore @@ -0,0 +1,17 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/bindings/rust/icsneoc2rs/Cargo.lock b/bindings/rust/icsneoc2rs/Cargo.lock new file mode 100644 index 0000000..9904baf --- /dev/null +++ b/bindings/rust/icsneoc2rs/Cargo.lock @@ -0,0 +1,293 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "icsneoc2rs" +version = "0.1.0" +dependencies = [ + "bindgen", + "scopeguard", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "log" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "prettyplease" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/bindings/rust/icsneoc2rs/Cargo.toml b/bindings/rust/icsneoc2rs/Cargo.toml new file mode 100644 index 0000000..ab6cb87 --- /dev/null +++ b/bindings/rust/icsneoc2rs/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "icsneoc2rs" +version = "0.1.0" +edition = "2021" + +[lib] +name = "icsneoc2rs" +crate-type = ["rlib", "staticlib", "cdylib"] +path = "src/lib.rs" + +[[example]] +name = "simple" +path = "examples/simple.rs" + +[dev-dependencies] +scopeguard = "1.2.0" + +[features] +static = [] + +[build-dependencies] +bindgen = "0.71.1" diff --git a/bindings/rust/icsneoc2rs/build.rs b/bindings/rust/icsneoc2rs/build.rs new file mode 100644 index 0000000..6eeb03f --- /dev/null +++ b/bindings/rust/icsneoc2rs/build.rs @@ -0,0 +1,40 @@ +use std::env; +use std::path::PathBuf; + +pub fn main() { + // Tell cargo to look for shared libraries in the specified directory + println!("cargo:rustc-link-search=../../../build"); + + // Tell cargo to tell rustc to link the system + // shared library. + println!("cargo:rustc-link-lib=icsneoc2"); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("../../../include/icsneo/icsneoc2.h") + .clang_arg("-I../../../include") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .derive_default(true) + .derive_debug(true) + .derive_partialeq(true) + .derive_copy(true) + .default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true }) + .default_alias_style(bindgen::AliasVariation::TypeAlias) + .generate_cstr(true) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} \ No newline at end of file diff --git a/bindings/rust/icsneoc2rs/examples/simple.rs b/bindings/rust/icsneoc2rs/examples/simple.rs new file mode 100644 index 0000000..1d11f33 --- /dev/null +++ b/bindings/rust/icsneoc2rs/examples/simple.rs @@ -0,0 +1,333 @@ +use icsneoc2rs::ffi::_icsneoc2_msg_bus_type_t::*; +use icsneoc2rs::ffi::_icsneoc2_msg_type_t::*; +use icsneoc2rs::ffi::_icsneoc2_netid_t; +use icsneoc2rs::ffi::_icsneoc2_netid_t::*; +use icsneoc2rs::ffi::_icsneoc2_open_options_t::*; +use icsneoc2rs::ffi::icsneoc2_message_t; +use icsneoc2rs::ffi::icsneoc2_msg_bus_type_t; +use icsneoc2rs::ffi::icsneoc2_msg_type_t; +use icsneoc2rs::ffi::icsneoc2_netid_t; +use icsneoc2rs::Device; +use icsneoc2rs::Error; +use icsneoc2rs::Result as ICSNeoResult; + +use scopeguard::defer; + +fn main() -> ICSNeoResult<()> { + print!("Finding devices... "); + let devices = Device::find_all(255)?; + println!( + "OK, {} device{} found...", + devices.len(), + if devices.len() > 1 { "s" } else { "" } + ); + for device in devices { + device.device_is_valid()?; + // Get description of the device + let description = match device.device_description_get() { + Ok(description) => description, + Err(err) => { + println!("Failed to get description of device: {err}"); + continue; + } + }; + println!("{} @ Handle {:?}", description, device.handle); + // Get/Set open options + let mut open_options = match device.device_open_options_get() { + Ok(value) => value, + Err(err) => { + println!("Failed to get open options of device: {err}"); + continue; + } + }; + // Disable Syncing RTC and going online + open_options &= !(icsneoc2_open_options_sync_rtc as u32); + open_options &= !(icsneoc2_open_options_go_online as u32); + match device.device_open_options_set(open_options) { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("\tDevice open options: {open_options:#02x}"); + // Open the device + print!("\tOpening device... "); + match device.device_open() { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("OK"); + defer! { + print_device_events(&device, &description).expect("Critical: Failed to print device events"); + } + // Get timestamp resolution of the device + print!("\tGetting timestamp resolution... "); + let timestamp_resolution = match device.device_timestamp_resolution_get() { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("{timestamp_resolution}ns"); + // Get baudrates for HSCAN + print!("\tGetting HSCAN baudrate... "); + let baudrate = match device.device_baudrate_get(icsneoc2_netid_hscan) { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("{baudrate}mbit/s"); + // Get FD baudrates for HSCAN + print!("\tGetting FD HSCAN baudrate... "); + let fd_baudrate = match device.device_canfd_baudrate_get(icsneoc2_netid_hscan) { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("{fd_baudrate}mbit/s"); + // Set baudrates for HSCAN + // saveToDevice: If this is set to true, the baudrate will be saved on the device + // and will persist through a power cycle + print!("\tSetting HSCAN baudrate... "); + let save_to_device = false; + match device.device_baudrate_set(icsneoc2_netid_hscan, baudrate, save_to_device) { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("OK"); + // Set FD baudrates for HSCAN + print!("\tSetting FD HSCAN baudrate... "); + match device.device_canfd_baudrate_set(icsneoc2_netid_hscan, fd_baudrate, save_to_device) { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("OK"); + // Get RTC + print!("\tGetting RTC... "); + let current_rtc = match device.device_rtc_get() { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("{current_rtc}"); + // Set RTC + print!("\tSetting RTC... "); + match device.device_rtc_set(current_rtc) { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("OK"); + // Get RTC + print!("\tGetting RTC... "); + let rtc = match device.device_rtc_get() { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("{rtc}"); + // Go online, start acking traffic + print!("\tGoing online... "); + match device.device_go_online(true) { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + // Redundant check to show how to check if the device is online, if the previous + // icsneoc2_device_go_online call was successful we can assume we are online already + let is_online = match device.device_is_online() { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("{}", if is_online { "Online" } else { "Offline" }); + // Transmit CAN messages + if transmit_can_messages(&device).is_err() { + continue; + } + println!("\tWaiting 1 second for messages..."); + std::thread::sleep(std::time::Duration::from_secs(1)); + // Get the messages + let messages = match device.device_messages_get(20000, 3000) { + Ok(messages) => messages, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + // Process the messages + if process_messages(&device, messages).is_err() { + continue; + } + // Close the device + print!("\tClosing device... "); + match device.device_close() { + Ok(value) => value, + Err(err) => { + print_error(&device, &description, err); + continue; + } + }; + println!("OK"); + } + Ok(()) +} + +fn print_error(device: &Device, device_description: &String, err: Error) { + println!("Failed: {err}"); + print_device_events(device, device_description) + .expect("Critical: Failed to print device events"); +} + +fn transmit_can_messages(device: &Device) -> ICSNeoResult<()> { + const MESSAGE_COUNT: usize = 100; + println!("\tTransmitting {MESSAGE_COUNT} messages..."); + for i in 0..MESSAGE_COUNT { + let message = match device.message_can_create(1) { + Ok(messages) => messages[0], + Err(err) => { + println!("Failed to create CAN message #{i}: {err}"); + continue; + } + }; + device.message_netid_set(message, icsneoc2_netid_hscan)?; + device.message_can_arbid_set(message, 0x10)?; + device.message_can_canfd_set(message, true)?; + device.message_can_extended_set(message, true)?; + device.message_can_baudrate_switch_set(message, true)?; + device.message_data_set( + message, + vec![ + (i >> 56 & 0xFF) as u8, + (i >> 48 & 0xFF) as u8, + (i >> 40 & 0xFF) as u8, + (i >> 32 & 0xFF) as u8, + (i >> 24 & 0xFF) as u8, + (i >> 16 & 0xFF) as u8, + (i >> 8 & 0xFF) as u8, + (i & 0xFF) as u8, + ], + )?; + device.message_can_dlc_set(message, -1)?; + device.device_messages_transmit(vec![message])?; + } + Ok(()) +} + +fn process_messages(device: &Device, messages: Vec<*mut icsneoc2_message_t>) -> ICSNeoResult<()> { + let mut tx_count: u32 = 0; + for (i, message) in messages.iter().enumerate() { + // Get the message type + let msg_type = device.message_type_get(*message)?; + // Get the message type name + let msg_type_name = device.message_type_name_get(msg_type)?; + // Check if the message is a bus message, ignore otherwise + if msg_type != icsneoc2_msg_type_bus as icsneoc2_msg_type_t { + println!("\tIgnoring message type : {msg_type} ({msg_type_name})"); + continue; + } + // Get the message bus type + let msg_bus_type = device.message_bus_type_get(*message)?; + // Get the message type name + let msg_bus_type_name = device.bus_type_name_get(msg_bus_type)?; + // Check if message is a transmit message + if device.message_is_transmit(*message)? { + tx_count += 1; + continue; + } + println!("\t{i} Message type: {msg_type} bus type: {msg_bus_type_name} ({msg_bus_type})\n"); + // Check if the message is a CAN message, ignore otherwise + if msg_bus_type == icsneoc2_msg_bus_type_can as icsneoc2_msg_bus_type_t { + let netid = device.message_netid_get(*message)?; + let netid_name = device.netid_name_get(netid)?; + let arbid = device.message_can_arbid_get(*message)?; + let dlc = device.message_can_dlc_get(*message)?; + let is_remote = device.message_can_is_remote(*message)?; + let is_canfd = device.message_can_is_canfd(*message)?; + let is_extended = device.message_can_is_extended(*message)?; + let data = device.message_data_get(*message, 64)?; + // Finally lets print the RX message + println!("\t NetID: {netid_name} ({:#02x})\tArbID: {arbid:#02x}\t DLC: {dlc}\t Remote: {is_remote}\t \ + CANFD: {is_canfd}\t Extended: {is_extended}\t Data length: {}\n", netid as icsneoc2_netid_t,data.len()); + println!("\t Data: {data:#02x?}\n"); + } else { + println!("\tIgnoring bus message type: {msg_bus_type} ({msg_bus_type_name})\n"); + } + } + println!( + "\tReceived {} messages total, {} were TX messages", + messages.len(), + tx_count + ); + Ok(()) +} + +fn print_device_events(device: &Device, device_description: &String) -> ICSNeoResult<()> { + let events = match device.device_events_get(1024) { + Ok(events) => events, + Err(err) => { + println!("Failed to get device events: {err}"); + return Err(err); + } + }; + for (i, event) in events.iter().enumerate() { + let description = match device.event_description_get(*event) { + Ok(description) => description, + Err(err) => { + println!("Failed to get event description: {err}"); + continue; + } + }; + println!("\t{device_description}: Event {i}: {description}"); + } + // Get global events + let global_events = match device.events_get(1024) { + Ok(events) => events, + Err(err) => { + println!("Failed to get global events: {err}"); + return Err(err); + } + }; + for (i, event) in global_events.iter().enumerate() { + let description = match device.event_description_get(*event) { + Ok(description) => description, + Err(err) => { + println!("Failed to get event description: {err}"); + continue; + } + }; + println!("\t{device_description}: Global Event {i}: {description}"); + } + println!( + "\t{device_description}: Received {} events and {} global events\n", + events.len(), + global_events.len() + ); + + Ok(()) +} diff --git a/bindings/rust/icsneoc2rs/src/ffi.rs b/bindings/rust/icsneoc2rs/src/ffi.rs new file mode 100644 index 0000000..428a22c --- /dev/null +++ b/bindings/rust/icsneoc2rs/src/ffi.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); \ No newline at end of file diff --git a/bindings/rust/icsneoc2rs/src/lib.rs b/bindings/rust/icsneoc2rs/src/lib.rs new file mode 100644 index 0000000..c519d50 --- /dev/null +++ b/bindings/rust/icsneoc2rs/src/lib.rs @@ -0,0 +1,902 @@ +pub mod ffi; + +use ffi::_icsneoc2_error_t::{icsneoc2_error_invalid_device, icsneoc2_error_success}; +use ffi::_icsneoc2_open_options_t::*; +use ffi::*; + +use std::ffi::CString; +use std::fmt::Display; + +#[derive(Debug, Clone)] +pub enum Error { + /// icsneoc2 API error + APIError(icsneoc2_error_t, String), +} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::APIError(error_code, error_string) => write!(f, "API Error: \"{}\" ({})", error_string, error_code), + } + } +} + +pub type Result = core::result::Result; + +#[derive(Debug, Clone)] +pub struct Device { + /// Handle to the device. + pub handle: *mut icsneoc2_device_t, +} + +impl Drop for Device { + fn drop(&mut self) { + // Make sure the device is closed before we go out of scope. + self.device_close().expect("Failed to close device!"); + } +} + +impl Device { + pub fn find_all(device_count: u32) -> Result> { + // Find all the devices + const MAX_DEVICE_COUNT: u32 = 255; + let mut devices: [*mut icsneoc2_device_t; MAX_DEVICE_COUNT as usize] = + [std::ptr::null_mut(); 255]; + let mut devices_count: u32 = if device_count > MAX_DEVICE_COUNT { + MAX_DEVICE_COUNT + } else { + device_count + }; + let res = unsafe { + icsneoc2_device_find_all( + devices.as_mut_ptr(), + &mut devices_count as *mut u32, + std::ptr::null_mut(), + ) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + // Return the results + let found_devices: Vec = devices[..devices_count as usize] + .iter() + .map(|d| Self { handle: *d }) + .collect(); + Ok(found_devices) + } + + pub fn bus_type_name_get(&self, bus_type: icsneoc2_msg_bus_type_t) -> Result { + // Get the string + let mut str: Vec = vec![0; 255]; + let mut str_length: u32 = 255; + let res = unsafe { + icsneoc2_bus_type_name_get(bus_type, str.as_mut_ptr() as *mut i8, &mut str_length) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + // Convert the vec to an String + str.resize(str_length as usize, 0); + let str_string = { + CString::new(str) + .expect("CString::new failed") + .into_string() + .expect("CString::new::into_string") + }; + Ok(str_string) + } + + pub fn device_baudrate_get(&self, netid: _icsneoc2_netid_t) -> Result { + let mut value: u64 = 0; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_baudrate_get(self.handle, netid as icsneoc2_netid_t, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn device_baudrate_set( + &self, + netid: _icsneoc2_netid_t, + value: u64, + save: bool, + ) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_baudrate_set(self.handle, netid as icsneoc2_netid_t, value, save) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn device_canfd_baudrate_get(&self, netid: _icsneoc2_netid_t) -> Result { + let mut value: u64 = 0; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_canfd_baudrate_get(self.handle, netid as icsneoc2_netid_t, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn device_canfd_baudrate_set( + &self, + netid: _icsneoc2_netid_t, + value: u64, + save: bool, + ) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_canfd_baudrate_set(self.handle, netid as icsneoc2_netid_t, value, save) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn device_close(&self) -> Result<()> { + let res: icsneoc2_error_t = unsafe { ffi::icsneoc2_device_close(self.handle) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn device_description_get(&self) -> Result { + // Get the error code + let mut description: Vec = vec![0; 255]; + let mut description_length: u32 = 255; + let res = unsafe { + icsneoc2_device_description_get( + self.handle, + description.as_mut_ptr() as *mut i8, + &mut description_length, + ) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + // Convert the vec to an String + description.resize(description_length as usize, 0); + let description_str = { + CString::new(description) + .expect("CString::new failed") + .into_string() + .expect("CString::new::into_string") + }; + Ok(description_str) + } + + pub fn device_events_get(&self, event_count: u32) -> Result> { + let mut events: Vec<*mut icsneoc2_event_t> = + vec![std::ptr::null_mut(); event_count as usize]; + let mut event_count: u32 = event_count; + let res: icsneoc2_error_t = unsafe { + ffi::icsneoc2_device_events_get(self.handle, events.as_mut_ptr(), &mut event_count) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + events.resize(event_count as usize, std::ptr::null_mut()); + Ok(events) + } + + pub fn device_go_online(&self, go_online: bool) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_go_online(self.handle, go_online) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn device_is_online(&self) -> Result { + let mut is_online: bool = false; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_is_online(self.handle, &mut is_online) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(is_online) + } + + pub fn device_is_online_supported(&self) -> Result { + let mut is_online_supported: bool = false; + let res: icsneoc2_error_t = unsafe { + ffi::icsneoc2_device_is_online_supported(self.handle, &mut is_online_supported) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(is_online_supported) + } + + pub fn device_is_valid(&self) -> Result { + let res: icsneoc2_error_t = unsafe { icsneoc2_device_is_valid(self.handle) }; + match res { + res if res == icsneoc2_error_success as icsneoc2_error_t => Ok(true), + res if res == icsneoc2_error_invalid_device as icsneoc2_error_t => Ok(false), + _ => Err(Self::error_code_get(res)), + } + } + + pub fn device_is_open(&self) -> Result { + let mut is_open: bool = false; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_is_open(self.handle, &mut is_open) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(is_open) + } + + pub fn device_is_disconnected(&self) -> Result { + let mut is_disconnected: bool = false; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_is_disconnected(self.handle, &mut is_disconnected) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(is_disconnected) + } + + pub fn device_load_default_settings(&self, save: bool) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_load_default_settings(self.handle, save) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn device_message_count_get(&self) -> Result { + let mut value: u32 = 0; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_message_count_get(self.handle, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn device_message_polling_get(&self) -> Result { + let mut value: bool = false; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_message_polling_get(self.handle, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn device_message_polling_limit_get(&self) -> Result { + let mut value: u32 = 0; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_message_polling_limit_get(self.handle, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn device_message_polling_set(&self, enable: bool) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_message_polling_set(self.handle, enable) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn device_message_polling_set_limit(&self, value: u32) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_message_polling_set_limit(self.handle, value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn device_messages_get( + &self, + message_count: u32, + timeout_ms: u32, + ) -> Result> { + let mut messages: Vec<*mut icsneoc2_message_t> = + vec![std::ptr::null_mut(); message_count as usize]; + let mut message_count: u32 = message_count; + let res: icsneoc2_error_t = unsafe { + ffi::icsneoc2_device_messages_get( + self.handle, + messages.as_mut_ptr(), + &mut message_count, + timeout_ms, + ) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + messages.resize(message_count as usize, std::ptr::null_mut()); + Ok(messages) + } + + pub fn device_messages_transmit(&self, messages: Vec<*mut icsneoc2_message_t>) -> Result { + let mut messages_count: u32 = messages.len() as u32; + let mut messages = messages; + let res: icsneoc2_error_t = unsafe { + ffi::icsneoc2_device_messages_transmit( + self.handle, + messages.as_mut_ptr(), + &mut messages_count, + ) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(messages_count) + } + + pub fn device_open(&self) -> Result<()> { + let res: icsneoc2_error_t = unsafe { ffi::icsneoc2_device_open(self.handle) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn device_open_options_get(&self) -> Result { + let mut open_options: ffi::icsneoc2_open_options_t = + icsneoc2_open_options_none as ffi::icsneoc2_open_options_t; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_open_options_get(self.handle, &mut open_options) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(open_options) + } + + pub fn device_open_options_set( + &self, + open_options: ffi::icsneoc2_open_options_t, + ) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_open_options_set(self.handle, open_options) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn device_rtc_get(&self) -> Result { + let mut value: i64 = 0; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_rtc_get(self.handle, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn device_rtc_set(&self, value: i64) -> Result<()> { + let res: icsneoc2_error_t = unsafe { ffi::icsneoc2_device_rtc_set(self.handle, value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn device_serial_get(&self) -> Result { + // Get the string + let mut str: Vec = vec![0; 255]; + let mut str_length: u32 = 255; + let res = unsafe { + icsneoc2_device_serial_get(self.handle, str.as_mut_ptr() as *mut i8, &mut str_length) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + // Convert the vec to an String + str.resize(str_length as usize, 0); + let str_string = { + CString::new(str) + .expect("CString::new failed") + .into_string() + .expect("CString::new::into_string") + }; + Ok(str_string) + } + + pub fn device_supports_tc10(&self) -> Result { + let mut value: bool = false; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_supports_tc10(self.handle, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn device_timestamp_resolution_get(&self) -> Result { + let mut timestamp_resolution: u32 = 0; + let res: icsneoc2_error_t = unsafe { + ffi::icsneoc2_device_timestamp_resolution_get(self.handle, &mut timestamp_resolution) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(timestamp_resolution) + } + + pub fn device_type_from_type(&self, device_type: icsneoc2_devicetype_t) -> Result { + // Get the string + let mut str: Vec = vec![0; 255]; + let mut str_length: u32 = 255; + let res = unsafe { + icsneoc2_device_type_name_get( + device_type, + str.as_mut_ptr() as *mut i8, + &mut str_length, + ) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + // Convert the vec to an String + str.resize(str_length as usize, 0); + let str_string = { + CString::new(str) + .expect("CString::new failed") + .into_string() + .expect("CString::new::into_string") + }; + Ok(str_string) + } + + pub fn device_type_get(&self) -> Result { + let mut value: icsneoc2_devicetype_t = 0; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_device_type_get(self.handle, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn error_code_get(error_code: icsneoc2_error_t) -> Error { + // Get the error code + let mut error_description: Vec = vec![0; 255]; + let mut error_description_length: u32 = 255; + let res = unsafe { + icsneoc2_error_code_get( + error_code, + error_description.as_mut_ptr() as *mut i8, + &mut error_description_length, + ) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Error::APIError(res, "icsneoc2_error_code_get() failed.".to_string()); + } + // Convert the vec to an String + error_description.resize(error_description_length as usize, 0); + let error_str = CString::new(error_description) + .expect("CString::new failed") + .into_string() + .expect("CString::new::into_string"); + Error::APIError(error_code, error_str) + } + + pub fn event_description_get(&self, event: *mut icsneoc2_event_t) -> Result { + // Get the string + let mut str: Vec = vec![0; 255]; + let mut str_length: u32 = 255; + let res = unsafe { + icsneoc2_event_description_get(event, str.as_mut_ptr() as *mut i8, &mut str_length) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + // Convert the vec to an String + str.resize(str_length as usize, 0); + let str_string = { + CString::new(str) + .expect("CString::new failed") + .into_string() + .expect("CString::new::into_string") + }; + Ok(str_string) + } + + pub fn events_get(&self, event_count: u32) -> Result> { + let mut events: Vec<*mut icsneoc2_event_t> = + vec![std::ptr::null_mut(); event_count as usize]; + let mut event_count: u32 = event_count; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_events_get(events.as_mut_ptr(), &mut event_count) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + events.resize(event_count as usize, std::ptr::null_mut()); + Ok(events) + } + + pub fn message_bus_type_get( + &self, + message: *mut icsneoc2_message_t, + ) -> Result { + let mut value: icsneoc2_msg_bus_type_t = 0; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_bus_type_get(self.handle, message, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn message_can_arbid_get(&self, message: *mut icsneoc2_message_t) -> Result { + let mut value: u32 = 0; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_can_arbid_get(self.handle, message, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn message_can_arbid_set( + &self, + message: *mut icsneoc2_message_t, + value: u32, + ) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_can_arbid_set(self.handle, message, value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn message_can_baudrate_switch_get( + &self, + message: *mut icsneoc2_message_t, + ) -> Result { + let mut value: bool = false; + let res: icsneoc2_error_t = unsafe { + ffi::icsneoc2_message_can_baudrate_switch_get(self.handle, message, &mut value) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn message_can_baudrate_switch_set( + &self, + message: *mut icsneoc2_message_t, + value: bool, + ) -> Result<()> { + let res: icsneoc2_error_t = unsafe { + ffi::icsneoc2_message_can_baudrate_switch_set(self.handle, message, value) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn message_can_canfd_set( + &self, + message: *mut icsneoc2_message_t, + value: bool, + ) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_can_canfd_set(self.handle, message, value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn message_can_create(&self, message_count: u32) -> Result> { + let mut messages: Vec<*mut icsneoc2_message_t> = + vec![std::ptr::null_mut(); message_count as usize]; + let res: icsneoc2_error_t = unsafe { + ffi::icsneoc2_message_can_create(self.handle, messages.as_mut_ptr(), message_count) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(messages) + } + + pub fn message_can_dlc_get(&self, message: *mut icsneoc2_message_t) -> Result { + let mut value: i32 = 0; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_can_dlc_get(self.handle, message, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn message_can_dlc_set(&self, message: *mut icsneoc2_message_t, value: i32) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_can_dlc_set(self.handle, message, value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn message_can_error_state_indicator_get( + &self, + message: *mut icsneoc2_message_t, + ) -> Result { + let mut value: bool = false; + let res: icsneoc2_error_t = unsafe { + ffi::icsneoc2_message_can_error_state_indicator_get(self.handle, message, &mut value) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn message_can_extended_set( + &self, + message: *mut icsneoc2_message_t, + value: bool, + ) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_can_extended_set(self.handle, message, value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn message_can_free(&self, message: *mut icsneoc2_message_t) -> Result<()> { + let res: icsneoc2_error_t = unsafe { ffi::icsneoc2_message_can_free(self.handle, message) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn message_can_is_canfd(&self, message: *mut icsneoc2_message_t) -> Result { + let mut value: bool = false; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_can_is_canfd(self.handle, message, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn message_can_is_extended(&self, message: *mut icsneoc2_message_t) -> Result { + let mut value: bool = false; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_can_is_extended(self.handle, message, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn message_can_is_remote(&self, message: *mut icsneoc2_message_t) -> Result { + let mut value: bool = false; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_can_is_remote(self.handle, message, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn message_data_get( + &self, + message: *mut icsneoc2_message_t, + data_length: u32, + ) -> Result> { + let mut data: Vec = vec![0; data_length as usize]; + let mut data_length = data_length; + let res: icsneoc2_error_t = unsafe { + ffi::icsneoc2_message_data_get( + self.handle, + message, + data.as_mut_ptr(), + &mut data_length, + ) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + data.resize(data_length as usize, 0); + Ok(data) + } + + pub fn message_data_set(&self, message: *mut icsneoc2_message_t, data: Vec) -> Result<()> { + let mut data = data; + let res: icsneoc2_error_t = unsafe { + ffi::icsneoc2_message_data_set( + self.handle, + message, + data.as_mut_ptr(), + data.len() as u32, + ) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn message_is_transmit(&self, message: *mut icsneoc2_message_t) -> Result { + let mut value: bool = false; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_is_transmit(self.handle, message, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn message_is_valid(&self, message: *mut icsneoc2_message_t) -> Result { + let mut value: bool = false; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_is_valid(self.handle, message, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn message_netid_get(&self, message: *mut icsneoc2_message_t) -> Result<_icsneoc2_netid_t> { + let mut value: icsneoc2_netid_t = 0; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_netid_get(self.handle, message, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + // TODO: This is gross, there is probably a better way to do this. + // TryFrom impl would be a lot of work to implement because there are so many values. + // We are relying on the function call to always return a valid value. + let value: _icsneoc2_netid_t = unsafe { std::mem::transmute(value as u32) }; + Ok(value) + } + + pub fn message_netid_set( + &self, + message: *mut icsneoc2_message_t, + value: _icsneoc2_netid_t, + ) -> Result<()> { + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_netid_set(self.handle, message, value as icsneoc2_netid_t) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(()) + } + + pub fn message_type_get( + &self, + message: *mut icsneoc2_message_t, + ) -> Result { + let mut value: icsneoc2_msg_type_t = 0; + let res: icsneoc2_error_t = + unsafe { ffi::icsneoc2_message_type_get(self.handle, message, &mut value) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + Ok(value) + } + + pub fn message_type_name_get(&self, msg_type: icsneoc2_msg_type_t) -> Result { + // Get the string + let mut str: Vec = vec![0; 255]; + let mut str_length: u32 = 255; + let res = unsafe { + icsneoc2_message_type_name_get(msg_type, str.as_mut_ptr() as *mut i8, &mut str_length) + }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + // Convert the vec to an String + str.resize(str_length as usize, 0); + let str_string = { + CString::new(str) + .expect("CString::new failed") + .into_string() + .expect("CString::new::into_string") + }; + Ok(str_string) + } + + pub fn netid_name_get(&self, netid: _icsneoc2_netid_t) -> Result { + // Get the string + let mut str: Vec = vec![0; 255]; + let mut str_length: u32 = 255; + let res = + unsafe { icsneoc2_netid_name_get(netid as icsneoc2_netid_t, str.as_mut_ptr() as *mut i8, &mut str_length) }; + // Check the error code + if res != icsneoc2_error_success as icsneoc2_error_t { + return Err(Self::error_code_get(res)); + } + // Convert the vec to an String + str.resize(str_length as usize, 0); + let str_string = { + CString::new(str) + .expect("CString::new failed") + .into_string() + .expect("CString::new::into_string") + }; + Ok(str_string) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_device_find_all() { + let devices = Device::find_all(0).unwrap(); + assert_eq!(devices.len(), 0); + } +} diff --git a/communication/decoder.cpp b/communication/decoder.cpp index 101777f..3381610 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -89,7 +89,7 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrtimestamp *= timestampResolution; switch(result->type) { - case Message::Type::Frame: { + case Message::Type::BusMessage: { CANMessage& can = *static_cast(result.get()); can.network = packet->network; break; @@ -233,7 +233,7 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata.size() < 24) { - auto rawmsg = std::make_shared(Network::NetID::Device); + auto rawmsg = std::make_shared(Network::NetID::Device); result = rawmsg; rawmsg->data = packet->data; return true; @@ -263,7 +263,7 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptr(packet->network, packet->data); + result = std::make_shared(packet->network, packet->data); return true; } case Network::NetID::RED_INT_MEMORYREAD: { @@ -324,7 +324,7 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptr& result, const std::shared_ptr(packet->network, packet->data); + result = std::make_shared(packet->network, packet->data); return true; } diff --git a/communication/encoder.cpp b/communication/encoder.cpp index 25c10dc..ffe706a 100644 --- a/communication/encoder.cpp +++ b/communication/encoder.cpp @@ -23,8 +23,8 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector& result, result.clear(); switch(message->type) { - case Message::Type::Frame: { - auto frame = std::dynamic_pointer_cast(message); + case Message::Type::BusMessage: { + auto frame = std::dynamic_pointer_cast(message); // Frame uses frame->data as the buffer unless directed otherwise buffer = &frame->data; @@ -130,8 +130,8 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector& result, break; } - case Message::Type::RawMessage: { - auto raw = std::dynamic_pointer_cast(message); + case Message::Type::InternalMessage: { + auto raw = std::dynamic_pointer_cast(message); // Raw message uses raw->data as the buffer unless directed otherwise buffer = &raw->data; @@ -243,7 +243,7 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector& result, * In this case, command 0x06 is SetLEDState. * This old command type is not really used anywhere else. */ - auto canmsg = std::make_shared(Network::NetID::Device); + auto canmsg = std::make_shared(Network::NetID::Device); msg = canmsg; if(arguments.empty()) { report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error); diff --git a/communication/message/callback/streamoutput/a2bwavoutput.cpp b/communication/message/callback/streamoutput/a2bwavoutput.cpp index f5830bd..df4a661 100644 --- a/communication/message/callback/streamoutput/a2bwavoutput.cpp +++ b/communication/message/callback/streamoutput/a2bwavoutput.cpp @@ -102,11 +102,11 @@ bool A2BWAVOutput::callIfMatch(const std::shared_ptr& message) const { return false; } - if(message->type != Message::Type::Frame) { + if(message->type != Message::Type::BusMessage) { return false; } - const auto& frameMsg = std::dynamic_pointer_cast(message); + const auto& frameMsg = std::dynamic_pointer_cast(message); if(!frameMsg) { return false; diff --git a/communication/message/neomessage.cpp b/communication/message/neomessage.cpp index 08fdfdd..c53a246 100644 --- a/communication/message/neomessage.cpp +++ b/communication/message/neomessage.cpp @@ -14,9 +14,9 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr message) { neomsg.timestamp = message->timestamp; switch (message->type) { - case Message::Type::Frame: { + case Message::Type::BusMessage: { neomessage_frame_t& frame = *(neomessage_frame_t*)&neomsg; - auto framemsg = std::static_pointer_cast(message); + auto framemsg = std::static_pointer_cast(message); const auto netType = framemsg->network.getType(); frame.netid = (neonetid_t)framemsg->network.getNetID(); @@ -120,7 +120,7 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr message) { std::shared_ptr icsneo::CreateMessageFromNeoMessage(const neomessage_t* neomessage) { switch((Message::Type)neomessage->messageType) { - case Message::Type::Frame: { + case Message::Type::BusMessage: { const Network network = ((neomessage_frame_t*)neomessage)->netid; switch(network.getType()) { case Network::Type::CAN: diff --git a/communication/packet/canpacket.cpp b/communication/packet/canpacket.cpp index 0601df8..5713b33 100644 --- a/communication/packet/canpacket.cpp +++ b/communication/packet/canpacket.cpp @@ -3,7 +3,7 @@ using namespace icsneo; -static std::optional CAN_DLCToLength(uint8_t length, bool fd) { +std::optional icsneo::CAN_DLCToLength(uint8_t length, bool fd) { if (length <= 8) return length; @@ -29,7 +29,7 @@ static std::optional CAN_DLCToLength(uint8_t length, bool fd) { return std::nullopt; } -static std::optional CAN_LengthToDLC(size_t dataLength, bool fd) +std::optional icsneo::CAN_LengthToDLC(size_t dataLength, bool fd) { if (dataLength <= 8) return uint8_t(dataLength); diff --git a/device/device.cpp b/device/device.cpp index 351044d..2f302b8 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -480,7 +480,7 @@ int8_t Device::prepareScriptLoad() { return false; } - const auto resp = std::static_pointer_cast(generic); + const auto resp = std::static_pointer_cast(generic); retVal = (int8_t)resp->data[0]; } @@ -743,7 +743,7 @@ std::optional Device::readCoreminiHeader(Disk::MemoryType memTyp return ret; } -bool Device::transmit(std::shared_ptr frame) { +bool Device::transmit(std::shared_ptr frame) { if(!isOpen()) { report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error); return false; @@ -776,7 +776,7 @@ bool Device::transmit(std::shared_ptr frame) { return com->sendPacket(packet); } -bool Device::transmit(std::vector> frames) { +bool Device::transmit(std::vector> frames) { for(auto& frame : frames) { if(!transmit(frame)) return false; @@ -1721,8 +1721,8 @@ void Device::handleInternalMessage(std::shared_ptr message) { case Message::Type::ResetStatus: latestResetStatus = std::static_pointer_cast(message); break; - case Message::Type::RawMessage: { - auto rawMessage = std::static_pointer_cast(message); + case Message::Type::InternalMessage: { + auto rawMessage = std::static_pointer_cast(message); switch(rawMessage->network.getNetID()) { case Network::NetID::DeviceStatus: // Device Status format is unique per device, so the devices need to decode it themselves @@ -1733,8 +1733,8 @@ void Device::handleInternalMessage(std::shared_ptr message) { } break; } - case Message::Type::Frame: { - // Device is not guaranteed to be a CANMessage, it might be a RawMessage + case Message::Type::BusMessage: { + // Device is not guaranteed to be a CANMessage, it might be a InternalMessage // if it couldn't be decoded to a CANMessage. We only care about the // CANMessage decoding right now. auto canmsg = std::dynamic_pointer_cast(message); @@ -1898,7 +1898,7 @@ std::optional> Device::getRTC if(!generic) // Did not receive a message return std::nullopt; - auto rawMes = std::dynamic_pointer_cast(generic); + auto rawMes = std::dynamic_pointer_cast(generic); if(!rawMes) return std::nullopt; diff --git a/device/extensions/flexray/extension.cpp b/device/extensions/flexray/extension.cpp index 96e7d8d..4364294 100644 --- a/device/extensions/flexray/extension.cpp +++ b/device/extensions/flexray/extension.cpp @@ -45,7 +45,7 @@ void FlexRay::Extension::handleMessage(const std::shared_ptr& message) } } -bool FlexRay::Extension::transmitHook(const std::shared_ptr& frame, bool& success) { +bool FlexRay::Extension::transmitHook(const std::shared_ptr& frame, bool& success) { if(!frame || frame->network.getType() != Network::Type::FlexRay) return true; // Don't hook non-FlexRay messages diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6379bdb..2177c77 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,6 +1,7 @@ option(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE "Build the command-line interactive C example." ON) 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 command-line simple C 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) @@ -27,6 +28,10 @@ if(LIBICSNEO_BUILD_C_LEGACY_EXAMPLE) add_subdirectory(c/legacy) endif() +if(LIBICSNEO_BUILD_C2_SIMPLE_EXAMPLE) + add_subdirectory(c2/simple) +endif() + if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE) add_subdirectory(cpp/simple) endif() diff --git a/examples/c2/simple/CMakeLists.txt b/examples/c2/simple/CMakeLists.txt new file mode 100644 index 0000000..f0a9c12 --- /dev/null +++ b/examples/c2/simple/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(libicsneo-simple-example src/main.c) +target_link_libraries(libicsneo-simple-example icsneoc2) diff --git a/examples/c2/simple/src/main.c b/examples/c2/simple/src/main.c new file mode 100644 index 0000000..356dcd9 --- /dev/null +++ b/examples/c2/simple/src/main.c @@ -0,0 +1,454 @@ +#include + +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + + +/** + * @brief Sleeps for a specified number of milliseconds. + * + * Sleeps for a specified number of milliseconds using Sleep() on Windows and sleep() on *nix. + * + * @param ms The number of milliseconds to sleep. + */ +void sleep_ms(uint32_t ms) { +#if defined(_WIN32) || defined(_WIN64) + Sleep(ms); +#else + sleep(ms / 1000); +#endif +} + +/** + * @brief Prints an error message with the given string and error code. + * + * If the error code is not icsneoc2_error_success, prints the error string for the given error code + * and returns the error code. + * + * @param message The message to print. + * @param error The error code to print. + * @return error as int + */ +int print_error_code(const char* message, icsneoc2_error_t error) { + char error_str[256] = {0}; + uint32_t error_length = 256; + icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_length); + 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; +} + +/** + * @brief Processes a list of messages from a device. + * + * This function iterates over a given array of messages received from a specified device. + * For each message in the array, it retrieves and prints the message type and bus type. + * If an error occurs while retrieving these details, an error message is printed. + * + * @param device A pointer to the icsneoc2_device_t structure representing the device. + * @param messages An array of pointers to icsneoc2_message_t structures containing the messages to process. + * @param messages_count The number of messages in the messages array. + * + * @return An icsneoc2_error_t value indicating success or failure of the message processing. + */ +int process_messages(icsneoc2_device_t* device, icsneoc2_message_t** messages, uint32_t messages_count); + +/** + * @brief Prints device and global events for a given device. + * + * This function retrieves and prints all current events associated with the specified device, + * as well as any global events not tied to a specific device. For each event, it retrieves + * and prints a description. If retrieving events or their descriptions fails, an error + * message is printed. The function also prints a summary of the count of device-specific + * and global events processed. + * + * @param device A pointer to the icsneoc2_device_t structure representing the device to get events from. + * @param device_description A description of the device used in the output. + */ +void print_device_events(icsneoc2_device_t* device, const char* device_description); + +/** + * @brief Transmits a series of CAN messages from a device. + * + * This function creates and transmits 100 CAN messages with incrementing payload data. + * Each message is configured with specific attributes such as network ID, arbitration + * ID, CANFD status, extended status, and baudrate switch. After successfully transmitting + * each message, it is freed from memory. + * + * @param device A pointer to the icsneoc2_device_t structure representing the device to transmit messages from. + * + * @return An icsneoc2_error_t value indicating success or failure of the message transmission process. + */ +int transmit_can_messages(icsneoc2_device_t* device); + +/** + * @brief Get the RTC (Real time clock) of a device and print it. + * + * @param[in] device The device to get the RTC of. + * @param[in] description A description of the device for printing purpose. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +icsneoc2_error_t get_and_print_rtc(icsneoc2_device_t* device, const char* description); + +int main(int argc, char* argv[]) { + (void)argc; + (void)argv; + + icsneoc2_device_t* devices[255] = {0}; + uint32_t devices_count = 255; + + printf("Finding devices...\n"); + icsneoc2_error_t res = icsneoc2_device_find_all(devices, &devices_count, NULL); + if (res != icsneoc2_error_success) { + return print_error_code("\tFailed to find devices", res); + }; + printf("OK, %u device%s found\n", devices_count, devices_count == 1 ? "" : "s"); + // List off the devices + for (uint32_t i = 0; i < devices_count; i++) { + icsneoc2_device_t* device = devices[i]; + // Get description of the device + const char description[255] = {0}; + uint32_t description_length = 255; + res = icsneoc2_device_description_get(device, description, &description_length); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to get device description", res); + }; + printf("%.*s @ Handle %p\n", description_length, description, device); + // Get/Set open options + icsneoc2_open_options_t options = icsneoc2_open_options_none; + res = icsneoc2_device_open_options_get(device, &options); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to get open options", res); + } + // Disable Syncing RTC and going online + options &= ~icsneoc2_open_options_sync_rtc; + options &= ~icsneoc2_open_options_go_online; + printf("\tDevice open options: 0x%x\n", options); + res = icsneoc2_device_open_options_set(device, options); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to set open options", res); + } + // Open the device + printf("\tOpening device: %s...\n", description); + res = icsneoc2_device_open(device); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to open device", res); + }; + // Get timestamp resolution of the device + printf("\tGetting timestamp resolution... "); + uint32_t timestamp_resolution = 0; + res = icsneoc2_device_timestamp_resolution_get(device, ×tamp_resolution); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to get timestamp resolution", res); + } + printf("%uns\n", timestamp_resolution); + // Get baudrates for HSCAN + printf("\tGetting HSCAN Baudrate... "); + uint64_t baudrate = 0; + res = icsneoc2_device_baudrate_get(device, icsneoc2_netid_hscan, &baudrate); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to get baudrate", res); + }; + printf("%llumbit/s\n", baudrate); + // Get FDbaudrates for HSCAN + printf("\tGetting FD HSCAN Baudrate... "); + uint64_t fd_baudrate = 0; + res = icsneoc2_device_canfd_baudrate_get(device, icsneoc2_netid_hscan, &fd_baudrate); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to get FD baudrate", res); + }; + printf("%llumbit/s\n", fd_baudrate); + // Set baudrates for HSCAN + // save_to_device: If this is set to true, the baudrate will be saved on the device + // and will persist through a power cycle + bool save_to_device = false; + printf("\tSetting HSCAN Baudrate... "); + res = icsneoc2_device_baudrate_set(device, icsneoc2_netid_hscan, baudrate, save_to_device); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to set baudrate", res); + }; + printf("Ok\n"); + // Set FDbaudrates for HSCAN + printf("\tSetting FD HSCAN Baudrate... "); + res = icsneoc2_device_canfd_baudrate_set(device, icsneoc2_netid_hscan, fd_baudrate, save_to_device); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to set FD baudrate", res); + }; + printf("Ok\n"); + // Get RTC + printf("\tGetting RTC... "); + res = get_and_print_rtc(device, description); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to get RTC", res); + } + // Set RTC + printf("\tSetting RTC to current time... "); + time_t current_time = time(NULL); + res = icsneoc2_device_rtc_set(device, current_time); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to set RTC", res); + } + printf("Ok\n"); + // Get RTC + printf("\tGetting RTC... "); + res = get_and_print_rtc(device, description); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to get RTC", res); + } + // Go online, start acking traffic + printf("\tGoing online... "); + res = icsneoc2_device_go_online(device, true); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to go online", res); + } + // Redundant check to show how to check if the device is online, if the previous + // icsneoc2_device_go_online call was successful we can assume we are online already + bool is_online = false; + res = icsneoc2_device_is_online(device, &is_online); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to check if online", res); + } + printf("%s\n", is_online ? "Online" : "Offline"); + // Transmit CAN messages + res = transmit_can_messages(device); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + 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 + printf("\tWaiting 1 second for messages...\n"); + sleep_ms(1000); + // Get the messages + icsneoc2_message_t* messages[20000] = {0}; + uint32_t message_count = 20000; + printf("\tGetting messages from device with timeout of 3000ms on %s...\n", description); + res = icsneoc2_device_messages_get(device, messages, &message_count, 3000); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to get messages from device", res); + }; + // Process the messages + res = process_messages(device, messages, message_count); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to process messages", res); + } + // Finally, close the device. + printf("\tClosing device: %s...\n", description); + res = icsneoc2_device_close(device); + if (res != icsneoc2_error_success) { + print_device_events(device, description); + return print_error_code("\tFailed to close device", res); + }; + // Print device events + print_device_events(device, description); + + } + printf("\n"); + return 0; +} + +icsneoc2_error_t get_and_print_rtc(icsneoc2_device_t* device, const char* description) { + time_t unix_epoch = 0; + icsneoc2_error_t res = icsneoc2_device_rtc_get(device, &unix_epoch); + if (res != icsneoc2_error_success) { + return res; + } + char rtc_time[32] = {0}; + strftime(rtc_time, sizeof(rtc_time), "%Y-%m-%d %H:%M:%S", localtime(&unix_epoch)); + printf("RTC: %lld %s\n", unix_epoch, rtc_time); + + return icsneoc2_error_success; +} + +void print_device_events(icsneoc2_device_t* device, const char* device_description) { + // Get device events + icsneoc2_event_t* events[1024] = {0}; + uint32_t events_count = 1024; + icsneoc2_error_t res = icsneoc2_device_events_get(device, events, &events_count); + if (res != icsneoc2_error_success) { + (void)print_error_code("\tFailed to get device events", res); + return; + } + // Loop over each event and describe it. + for (uint32_t i = 0; i < events_count; i++) { + const char event_description[255] = {0}; + uint32_t event_description_length = 255; + res = icsneoc2_event_description_get(events[i], event_description, &event_description_length); + if (res != icsneoc2_error_success) { + print_error_code("\tFailed to get event description", res); + continue; + } + printf("\t%s: Event %u: %s\n", device_description, i, event_description); + } + + // Get global events + icsneoc2_event_t* global_events[1024] = {0}; + uint32_t global_events_count = 1024; + res = icsneoc2_events_get(global_events, &global_events_count); + if (res != icsneoc2_error_success) { + (void)print_error_code("\tFailed to get global events", res); + return; + } + // Loop over each event and describe it. + for (uint32_t i = 0; i < global_events_count; i++) { + const char event_description[255] = {0}; + uint32_t event_description_length = 255; + res = icsneoc2_event_description_get(global_events[i], event_description, &event_description_length); + if (res != icsneoc2_error_success) { + print_error_code("\tFailed to get global event description", res); + continue; + } + printf("\t%s: Global Event %u: %s\n", device_description, i, event_description); + } + printf("\t%s: Received %u events and %u global events\n", device_description, events_count, global_events_count); +} + +int process_messages(icsneoc2_device_t* device, icsneoc2_message_t** messages, uint32_t messages_count) { + // Print the type and bus type of each message + uint32_t tx_count = 0; + for (uint32_t i = 0; i < messages_count; i++) { + icsneoc2_message_t* message = messages[i]; + // Get the message type + icsneoc2_msg_type_t msg_type = 0; + icsneoc2_error_t res = icsneoc2_message_type_get(device, message, &msg_type); + if (res != icsneoc2_error_success) { + return print_error_code("\tFailed to get message type", res); + } + // Get the message type name + char msg_type_name[128] = {0}; + uint32_t msg_type_name_length = 128; + res = icsneoc2_message_type_name_get(msg_type, msg_type_name, &msg_type_name_length); + if (res != icsneoc2_error_success) { + return print_error_code("\tFailed to get message type name", res); + } + // Check if the message is a bus message, ignore otherwise + if (msg_type != icsneoc2_msg_type_bus) { + printf("Ignoring message type: %u (%s)\n", msg_type, msg_type_name); + continue; + } + icsneoc2_msg_bus_type_t bus_type = 0; + res = icsneoc2_message_bus_type_get(device, message, &bus_type); + if (res != icsneoc2_error_success) { + return print_error_code("\tFailed to get message bus type", res); + } + const char bus_name[128] = {0}; + uint32_t bus_name_length = 128; + res = icsneoc2_bus_type_name_get(bus_type, bus_name, &bus_name_length); + if (res != icsneoc2_error_success) { + return print_error_code("\tFailed to get message bus type name", res); + } + bool is_tx = false; + res = icsneoc2_message_is_transmit(device, message, &is_tx); + if (res != icsneoc2_error_success) { + return print_error_code("\tFailed to get message is transmit", res); + } + if (is_tx) { + tx_count++; + continue; + } + + printf("\t%d) Message type: %u bus type: %s (%u)\n", i, msg_type, bus_name, bus_type); + if (bus_type == icsneoc2_msg_bus_type_can) { + uint32_t arbid = 0; + int32_t dlc = 0; + icsneoc2_netid_t netid = 0; + bool is_remote = false; + bool is_canfd = false; + bool is_extended = false; + uint8_t data[64] = {0}; + uint32_t data_length = 64; + const char netid_name[128] = {0}; + uint32_t netid_name_length = 128; + uint32_t result = icsneoc2_message_netid_get(device, message, &netid); + result += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); + result += icsneoc2_message_can_arbid_get(device, message, &arbid); + result += icsneoc2_message_can_dlc_get(device, message, &dlc); + result += icsneoc2_message_can_is_remote(device, message, &is_remote); + result += icsneoc2_message_can_is_canfd(device, message, &is_canfd); + result += icsneoc2_message_can_is_extended(device, message, &is_extended); + result += icsneoc2_message_data_get(device, message, data, &data_length); + if (result != icsneoc2_error_success) { + printf("\tFailed get get CAN parameters (error: %u) for index %u\n", result, i); + continue; + } + printf("\t NetID: %s (0x%x)\tArbID: 0x%x\t DLC: %u\t Remote: %d\t CANFD: %d\t Extended: %d\t Data length: %u\n", netid_name, netid, arbid, dlc, is_remote, is_canfd, is_extended, data_length); + printf("\t Data: ["); + for (uint32_t x = 0; x < data_length; x++) { + printf(" 0x%x", data[x]); + } + printf(" ]\n"); + } + } + printf("\tReceived %u messages total, %u were TX messages\n", messages_count, tx_count); + + return icsneoc2_error_success; +} + +int transmit_can_messages(icsneoc2_device_t* device) { + uint64_t counter = 0; + const uint32_t msg_count = 100; + printf("\tTransmitting %d messages...\n", msg_count); + for (uint32_t i = 0; i < msg_count; i++) { + // Create the message + icsneoc2_message_t* message = NULL; + uint32_t message_count = 1; + icsneoc2_error_t res = icsneoc2_message_can_create(device, &message, message_count); + if (res != icsneoc2_error_success) { + return print_error_code("\tFailed to create messages", res); + } + // Set the message attributes + res = icsneoc2_message_netid_set(device, message, icsneoc2_netid_hscan); + res += icsneoc2_message_can_arbid_set(device, message, 0x10); + res += icsneoc2_message_can_canfd_set(device, message, true); + res += icsneoc2_message_can_extended_set(device, message, true); + res += icsneoc2_message_can_baudrate_switch_set(device, message, true); + // Create the payload + uint8_t data[8] = {0}; + data[0] = (uint8_t)(counter >> 56); + data[1] = (uint8_t)(counter >> 48); + data[2] = (uint8_t)(counter >> 40); + data[3] = (uint8_t)(counter >> 32); + data[4] = (uint8_t)(counter >> 24); + data[5] = (uint8_t)(counter >> 16); + data[6] = (uint8_t)(counter >> 8); + data[7] = (uint8_t)(counter >> 0); + res += icsneoc2_message_data_set(device, message, data, sizeof(data)); + res += icsneoc2_message_can_dlc_set(device, message, -1); + if (res != icsneoc2_error_success) { + return print_error_code("\tFailed to modify message", res); + } + res = icsneoc2_device_messages_transmit(device, &message, &message_count); + res += icsneoc2_message_can_free(device, message); + if (res != icsneoc2_error_success) { + return print_error_code("\tFailed to transmit messages", res); + } + counter++; + } + + return icsneoc2_error_success; +} diff --git a/examples/cpp/a2b/src/a2b.cpp b/examples/cpp/a2b/src/a2b.cpp index 9c3c9ca..09be6e2 100644 --- a/examples/cpp/a2b/src/a2b.cpp +++ b/examples/cpp/a2b/src/a2b.cpp @@ -259,8 +259,8 @@ void example4(const std::shared_ptr& rada2b) { auto handler = rada2b->addMessageCallback(std::make_shared( [] (std::shared_ptr newMsg) { - if(newMsg->type == icsneo::Message::Type::Frame) { - const auto& frame = std::dynamic_pointer_cast(newMsg); + if(newMsg->type == icsneo::Message::Type::BusMessage) { + const auto& frame = std::dynamic_pointer_cast(newMsg); if(frame && frame->network.getNetID() == icsneo::Network::NetID::I2C2) { const auto& i2cMessage = std::dynamic_pointer_cast(frame); diff --git a/examples/cpp/interactive/src/InteractiveExample.cpp b/examples/cpp/interactive/src/InteractiveExample.cpp index fc2628e..77e09d8 100644 --- a/examples/cpp/interactive/src/InteractiveExample.cpp +++ b/examples/cpp/interactive/src/InteractiveExample.cpp @@ -184,9 +184,9 @@ std::shared_ptr selectDevice(const std::vector& message) { switch(message->type) { - case icsneo::Message::Type::Frame: { + case icsneo::Message::Type::BusMessage: { // A message of type Frame is guaranteed to be a Frame, so we can static cast safely - auto frame = std::static_pointer_cast(message); + auto frame = std::static_pointer_cast(message); switch(frame->network.getType()) { case icsneo::Network::Type::CAN: { // A message of type CAN is guaranteed to be a CANMessage, so we can static cast safely @@ -264,7 +264,7 @@ void printMessage(const std::shared_ptr& message) { break; } break; - } // end of icsneo::Message::Type::Frame + } // end of icsneo::Message::Type::BusMessage case icsneo::Message::Type::CANErrorCount: { // A message of type CANErrorCount is guaranteed to be a CANErrorCount, so we can static cast safely auto cec = std::static_pointer_cast(message); diff --git a/examples/cpp/lin/src/LINExample.cpp b/examples/cpp/lin/src/LINExample.cpp index 64002ab..7dcd296 100644 --- a/examples/cpp/lin/src/LINExample.cpp +++ b/examples/cpp/lin/src/LINExample.cpp @@ -96,8 +96,8 @@ int main() { std::cout << "OK" << std::endl << std::endl; auto handler = device->addMessageCallback(std::make_shared([&](std::shared_ptr message) { - if(icsneo::Message::Type::Frame == message->type) { - auto frame = std::static_pointer_cast(message); + if(icsneo::Message::Type::BusMessage == message->type) { + auto frame = std::static_pointer_cast(message); if(icsneo::Network::Type::LIN == frame->network.getType()) { auto msg = std::static_pointer_cast(message); std::cout << msg->network << " RX frame | ID: 0x" << std::hex << static_cast(msg->ID) << " | "; diff --git a/examples/cpp/mdio/src/MDIOExample.cpp b/examples/cpp/mdio/src/MDIOExample.cpp index 5d64a98..8e6c6a0 100644 --- a/examples/cpp/mdio/src/MDIOExample.cpp +++ b/examples/cpp/mdio/src/MDIOExample.cpp @@ -92,8 +92,8 @@ int main() auto handler = device->addMessageCallback(std::make_shared([&](std::shared_ptr message) { - if(icsneo::Message::Type::Frame == message->type) { - auto frame = std::static_pointer_cast(message); + if(icsneo::Message::Type::BusMessage == message->type) { + auto frame = std::static_pointer_cast(message); if(icsneo::Network::Type::MDIO == frame->network.getType()) { auto msg = std::static_pointer_cast(message); std::cout << msg->network << " " << ((msg->isTXMsg)? "TX" : "RX") << " frame\n"; diff --git a/examples/cpp/simple/src/SimpleExample.cpp b/examples/cpp/simple/src/SimpleExample.cpp index adc92f5..ce21241 100644 --- a/examples/cpp/simple/src/SimpleExample.cpp +++ b/examples/cpp/simple/src/SimpleExample.cpp @@ -169,9 +169,9 @@ int main() { // MessageCallbacks are powerful, and can filter on things like ArbID for you. See the documentation auto handler = device->addMessageCallback(std::make_shared([](std::shared_ptr message) { switch(message->type) { - case icsneo::Message::Type::Frame: { - // A message of type Frame is guaranteed to be a Frame, so we can static cast safely - auto frame = std::static_pointer_cast(message); + case icsneo::Message::Type::BusMessage: { + // A message of type BusMessage is guaranteed to be a BusMessage, so we can static cast safely + auto frame = std::static_pointer_cast(message); switch(frame->network.getType()) { case icsneo::Network::Type::CAN: { // A message of type CAN is guaranteed to be a CANMessage, so we can static cast safely @@ -202,7 +202,7 @@ int main() { case icsneo::Network::Type::Ethernet: { auto ethMessage = std::static_pointer_cast(message); - std::cout << "\t\t" << ethMessage->network << " Frame - " << std::dec + std::cout << "\t\t" << ethMessage->network << " BusMessage - " << std::dec << ethMessage->data.size() << " bytes on wire\n"; std::cout << "\t\t Timestamped:\t"<< ethMessage->timestamp << " ns since 1/1/2007\n"; @@ -249,7 +249,7 @@ int main() { break; } break; - } // end of icsneo::Message::Type::Frame + } // end of icsneo::Message::Type::BusMessage case icsneo::Message::Type::CANErrorCount: { // A message of type CANErrorCount is guaranteed to be a CANErrorCount, so we can static cast safely auto cec = std::static_pointer_cast(message); diff --git a/examples/cpp/vsa/src/VSAExample.cpp b/examples/cpp/vsa/src/VSAExample.cpp index 5967786..29cb51e 100644 --- a/examples/cpp/vsa/src/VSAExample.cpp +++ b/examples/cpp/vsa/src/VSAExample.cpp @@ -16,13 +16,13 @@ void onEvent(std::shared_ptr event) { std::cout << event->describe() << std::endl; } -std::vector> constructRandomFrames(size_t frameCount, MessageType frameType) { +std::vector> constructRandomFrames(size_t frameCount, MessageType frameType) { static constexpr size_t ClassicCANSize = 8; static constexpr size_t CANFDSize = 64; static constexpr size_t ShortEthSize = 500; static constexpr size_t LongEthSize = 1500; - std::vector> frames; + std::vector> frames; std::random_device randDev; std::mt19937 randEngine(randDev()); std::uniform_int_distribution randByteDist(0,255); @@ -166,10 +166,10 @@ int main(int argc, char* argv[]) { uint64_t canFrameCount = 0; uint64_t ethFrameCount = 0; rxDevice->addMessageCallback(std::make_shared([&](std::shared_ptr msg) { - if(msg->type != icsneo::Message::Type::Frame) { + if(msg->type != icsneo::Message::Type::BusMessage) { return; } - const auto frame = std::static_pointer_cast(msg); + const auto frame = std::static_pointer_cast(msg); if(frame->network.getType() == icsneo::Network::Type::CAN) { ++canFrameCount; } else if(frame->network.getType() == icsneo::Network::Type::Ethernet) { @@ -200,7 +200,7 @@ int main(int argc, char* argv[]) { const uint8_t NumFrameTypes = 4; const size_t FrameCountPerType = 2500; - std::vector> frames; + std::vector> frames; for(uint8_t i = 0; i < NumFrameTypes; i++) { std::cout << "info: transmitting " << FrameCountPerType << " random " << MessageTypeLabels[i] << " frames" << std::endl; auto tempFrames = constructRandomFrames(FrameCountPerType, static_cast(i)); @@ -216,10 +216,10 @@ int main(int argc, char* argv[]) { size_t currentMessage = 0; rxDevice->addMessageCallback(std::make_shared([&](std::shared_ptr msg) { - if(msg->type != icsneo::Message::Type::Frame) { + if(msg->type != icsneo::Message::Type::BusMessage) { return; } - auto frame = std::static_pointer_cast(msg); + auto frame = std::static_pointer_cast(msg); if(frames[currentMessage]->data == frame->data) { currentMessage++; } diff --git a/examples/go/simple/README.md b/examples/go/simple/README.md new file mode 100644 index 0000000..3b99ef4 --- /dev/null +++ b/examples/go/simple/README.md @@ -0,0 +1,19 @@ +libicsneoc2 simple Go example +==== + +This is a mirror of the icsneoc2 C simple example, written in Go. + +Windows +==== + +- Install [msys64](https://www.msys2.org/) with gcc (`pacman -S mingw-w64-ucrt-x86_64-gcc`) +- Setup environment variables: + - add `C:\msys64\ucrt64\bin` to `PATH` + - Powershell: `$env:PATH += ";C:\msys64\ucrt64\bin"` + - `gcc --version` should return a version now + - enable cgo: `CGO_ENABLED = 1` + - Powershell: `$env:CGO_ENABLED=1` + - Set compiler to gcc + - Powershell: `$env:CC="gcc"` + - `icsneoc2.dll` should be in path (or inside this directory) +- `go run simple` diff --git a/examples/go/simple/go.mod b/examples/go/simple/go.mod new file mode 100644 index 0000000..3fecc93 --- /dev/null +++ b/examples/go/simple/go.mod @@ -0,0 +1,3 @@ +module simple + +go 1.23.4 diff --git a/examples/go/simple/simple.go b/examples/go/simple/simple.go new file mode 100644 index 0000000..a9ea2c0 --- /dev/null +++ b/examples/go/simple/simple.go @@ -0,0 +1,354 @@ +package main + +// #cgo CFLAGS: -I../../../include +// #cgo LDFLAGS: -L../../../build -licsneoc2 +// #include "icsneo/icsneoc2.h" +// #include "stdint.h" +import "C" +import ( + "fmt" + "time" + "unsafe" +) + +func main() { + // Find devices connected to host. + devices := [255]*C.icsneoc2_device_t{nil} + devicesCount := 255 + print("Finding devices... ") + if res := C.icsneoc2_device_find_all(&devices[0], (*C.uint)(unsafe.Pointer(&devicesCount)), nil); res != C.icsneoc2_error_success { + printError(res) + return + } + fmt.Printf("OK, %d device(s) found\n", devicesCount) + // List off the devices + for _, device := range devices[:devicesCount] { + // Get description of the device + description := make([]byte, 255) + descriptionLength := 255 + if res := C.icsneoc2_device_description_get(device, (*C.char)(unsafe.Pointer(&description[0])), (*C.uint)(unsafe.Pointer(&descriptionLength))); res != C.icsneoc2_error_success { + printError(res) + continue + } + fmt.Printf("%s @ Handle %p\n", description, device) + // Get/Set open options + options := C.icsneoc2_open_options_none + if res := C.icsneoc2_device_open_options_get(device, (*C.icsneoc2_open_options_t)(unsafe.Pointer(&options))); res != C.icsneoc2_error_success { + printError(res) + continue + } + options &= ^C.icsneoc2_open_options_sync_rtc + options &= ^C.icsneoc2_open_options_go_online + fmt.Printf("\tDevice open options: 0x%X\n", options) + if res := C.icsneoc2_device_open_options_set(device, (C.icsneoc2_open_options_t)(options)); res != C.icsneoc2_error_success { + printError(res) + continue + } + // Open the device + fmt.Printf("\tOpening device: %s...\n", description) + if res := C.icsneoc2_device_open(device); res != C.icsneoc2_error_success { + printError(res) + continue + } + defer func() { + if !printDeviceEvents(device, string(description)) { + println("\tFailed to print events...") + } + fmt.Printf("\tClosing device: %s...\n", description) + if res := C.icsneoc2_device_close(device); res != C.icsneoc2_error_success { + printError(res) + return + } + }() + // Get timestamp resolution of the device + fmt.Printf("\tGetting timestamp resolution... ") + var timestampResolution C.uint = 0 + if res := C.icsneoc2_device_timestamp_resolution_get(device, ×tampResolution); res != C.icsneoc2_error_success { + printError(res) + return + } + fmt.Printf("%dns\n", timestampResolution) + // Get baudrates for HSCAN + fmt.Printf("\tGetting HSCAN Baudrate... ") + var baudrate uint64 = 0 + if res := C.icsneoc2_device_baudrate_get(device, (C.icsneoc2_netid_t)(C.icsneoc2_netid_hscan), (*C.uint64_t)(unsafe.Pointer(&baudrate))); res != C.icsneoc2_error_success { + printError(res) + return + } + fmt.Printf("%dmbit/s\n", baudrate) + // Get FD baudrates for HSCAN + fmt.Printf("\tGetting FD HSCAN Baudrate... ") + var fdBaudrate uint64 = 0 + if res := C.icsneoc2_device_canfd_baudrate_get(device, (C.icsneoc2_netid_t)(C.icsneoc2_netid_hscan), (*C.uint64_t)(unsafe.Pointer(&fdBaudrate))); res != C.icsneoc2_error_success { + printError(res) + return + } + fmt.Printf("%dmbit/s\n", fdBaudrate) + // Set baudrates for HSCAN + // saveToDevice: If this is set to true, the baudrate will be saved on the device + // and will persist through a power cycle + var saveToDevice C.bool = false + fmt.Printf("\tSetting HSCAN Baudrate... ") + if res := C.icsneoc2_device_baudrate_set(device, (C.icsneoc2_netid_t)(C.icsneoc2_netid_hscan), (C.uint64_t)(baudrate), saveToDevice); res != C.icsneoc2_error_success { + printError(res) + return + } + fmt.Printf("OK\n") + // Set FD baudrates for HSCAN + fmt.Printf("\tSetting FD HSCAN Baudrate... ") + if res := C.icsneoc2_device_canfd_baudrate_set(device, (C.icsneoc2_netid_t)(C.icsneoc2_netid_hscan), (C.uint64_t)(fdBaudrate), saveToDevice); res != C.icsneoc2_error_success { + printError(res) + return + } + fmt.Printf("OK\n") + // Get RTC + fmt.Printf("\tGetting RTC... ") + var unix_epoch C.int64_t = 0 + if res := C.icsneoc2_device_rtc_get(device, (*C.int64_t)(unsafe.Pointer(&unix_epoch))); res != C.icsneoc2_error_success { + printError(res) + return + } + currentRTC := time.Unix(int64(unix_epoch), 0) + fmt.Printf("%d %s\n", currentRTC.Unix(), currentRTC) + // Set RTC + fmt.Printf("\tSetting RTC... ") + unix_epoch = (C.int64_t)(time.Now().Unix()) + if res := C.icsneoc2_device_rtc_set(device, unix_epoch); res != C.icsneoc2_error_success { + printError(res) + return + } + fmt.Printf("OK\n") + // Get RTC + fmt.Printf("\tGetting RTC... ") + if res := C.icsneoc2_device_rtc_get(device, (*C.int64_t)(unsafe.Pointer(&unix_epoch))); res != C.icsneoc2_error_success { + printError(res) + return + } + currentRTC = time.Unix(int64(unix_epoch), 0) + fmt.Printf("%d %s\n", currentRTC.Unix(), currentRTC) + // Go online, start acking traffic + fmt.Printf("\tGoing online... ") + if res := C.icsneoc2_device_go_online(device, true); res != C.icsneoc2_error_success { + printError(res) + return + } + // Redundant check to show how to check if the device is online, if the previous + // icsneoc2_device_go_online call was successful we can assume we are online already + var isOnline C.bool = false + if res := C.icsneoc2_device_is_online(device, &isOnline); res != C.icsneoc2_error_success { + printError(res) + return + } + if isOnline { + println("Online") + } else { + println("Offline") + } + // Transmit CAN messages + if !transmitCANMessages(device) { + return + } + // Wait for the bus to collect some messages, requires an active bus to get messages + println("\tWaiting 1 second for messages...") + time.Sleep(1 * time.Second) + // Get the messages + messages := [20000]*C.icsneoc2_message_t{nil} + var messagesCount C.uint32_t = 20000 + if res := C.icsneoc2_device_messages_get(device, &messages[0], &messagesCount, 3000); res != C.icsneoc2_error_success { + printError(res) + return + } + // Process the messages + if !processMessages(device, messages[0:messagesCount]) { + return + } + } +} + +func printError(err C.icsneoc2_error_t) C.icsneoc2_error_t { + buffer := make([]byte, 255) + bufferLength := 255 + res := C.icsneoc2_error_code_get(err, (*C.char)(unsafe.Pointer(&buffer[0])), (*C.uint)(unsafe.Pointer(&bufferLength))) + if res != C.icsneoc2_error_success { + println("\ticsneoc2_get_error_code failed, original error:", err) + return res + } + println("\tError:", string(buffer[:bufferLength])) + return res +} + +func printDeviceEvents(device *C.icsneoc2_device_t, deviceDescription string) bool { + // Get device events + events := [1024]*C.icsneoc2_event_t{nil} + var eventsCount C.uint32_t = 1024 + if res := C.icsneoc2_device_events_get(device, &events[0], &eventsCount); res != C.icsneoc2_error_success { + printError(res) + return false + } + for i, event := range events[:eventsCount] { + eventDescription := make([]byte, 255) + var eventDescriptionLength C.uint32_t = 255 + if res := C.icsneoc2_event_description_get(event, (*C.char)(unsafe.Pointer(&eventDescription[0])), &eventDescriptionLength); res != C.icsneoc2_error_success { + printError(res) + continue + } + fmt.Printf("\t%s: Event %d: %s\n", deviceDescription, i, eventDescription) + } + // Get global events + globalEvents := [1024]*C.icsneoc2_event_t{nil} + var globalEventsCount C.uint32_t = 1024 + if res := C.icsneoc2_events_get(&globalEvents[0], &globalEventsCount); res != C.icsneoc2_error_success { + printError(res) + return false + } + for i, event := range globalEvents[:globalEventsCount] { + globalEventsDescription := make([]byte, 255) + var globalEventsDescriptionLength C.uint32_t = 255 + if res := C.icsneoc2_event_description_get(event, (*C.char)(unsafe.Pointer(&globalEventsDescription[0])), &globalEventsDescriptionLength); res != C.icsneoc2_error_success { + printError(res) + continue + } + fmt.Printf("\t%s: Global Event %d: %s\n", deviceDescription, i, globalEventsDescription) + } + fmt.Printf("\t%s: Received %d events and %d global events\n", deviceDescription, eventsCount, globalEventsCount) + + return true +} + +func transmitCANMessages(device *C.icsneoc2_device_t) bool { + var counter uint32 = 0 + const msgCount int = 100 + fmt.Printf("\tTransmitting %d messages...\n", msgCount) + for range msgCount { + // Create the message + var message *C.icsneoc2_message_t = nil + if res := C.icsneoc2_message_can_create(device, &message, 1); res != C.icsneoc2_error_success { + printError(res) + return false + } + defer func() { + if res := C.icsneoc2_message_can_free(device, message); res != C.icsneoc2_error_success { + printError(res) + } + }() + // Set the message attributes + res := C.icsneoc2_message_netid_set(device, message, C.icsneoc2_netid_hscan) + res += C.icsneoc2_message_can_arbid_set(device, message, 0x10) + res += C.icsneoc2_message_can_canfd_set(device, message, true) + res += C.icsneoc2_message_can_extended_set(device, message, true) + res += C.icsneoc2_message_can_baudrate_switch_set(device, message, true) + // Create the payload + data := [...]C.uint8_t{ + (C.uint8_t)(counter >> 56), + (C.uint8_t)(counter >> 48), + (C.uint8_t)(counter >> 40), + (C.uint8_t)(counter >> 32), + (C.uint8_t)(counter >> 24), + (C.uint8_t)(counter >> 16), + (C.uint8_t)(counter >> 8), + (C.uint8_t)(counter >> 0), + } + res += C.icsneoc2_message_data_set(device, message, &data[0], (C.uint32_t)(len(data))) + res += C.icsneoc2_message_can_dlc_set(device, message, -1) + if res != C.icsneoc2_error_success { + fmt.Printf("\tFailed to modify message: %d\n", res) + return false + } + var messageCount C.uint32_t = 1 + if res := C.icsneoc2_device_messages_transmit(device, &message, &messageCount); res != C.icsneoc2_error_success { + printError(res) + return false + } + counter += 1 + } + return true +} + +func processMessages(device *C.icsneoc2_device_t, messages []*C.icsneoc2_message_t) bool { + txCount := 0 + for i, message := range messages { + // Get the message type + var msgType C.icsneoc2_msg_type_t = 0 + if res := C.icsneoc2_message_type_get(device, message, &msgType); res != C.icsneoc2_error_success { + printError(res) + return false + } + // Get the message type name + msgTypeName := make([]byte, 128) + var msgTypeNameLength C.uint32_t = 128 + if res := C.icsneoc2_message_type_name_get(msgType, (*C.char)(unsafe.Pointer(&msgTypeName[0])), &msgTypeNameLength); res != C.icsneoc2_error_success { + printError(res) + return false + } + // Check if the message is a bus message, ignore otherwise + if msgType != C.icsneoc2_msg_type_bus { + fmt.Print("\tIgnoring message type: %d (%s)\n", msgType, msgTypeName) + continue + } + // Get the message bus type + var msgBusType C.icsneoc2_msg_bus_type_t = 0 + if res := C.icsneoc2_message_bus_type_get(device, message, &msgBusType); res != C.icsneoc2_error_success { + printError(res) + return false + } + // Get the bus message type name + msgBusTypeName := make([]byte, 128) + var msgBusTypeNameLength C.uint32_t = 128 + if res := C.icsneoc2_bus_type_name_get(msgBusType, (*C.char)(unsafe.Pointer(&msgBusTypeName[0])), &msgBusTypeNameLength); res != C.icsneoc2_error_success { + printError(res) + return false + } + // Check if the message is a transmit message + var isTransmit C.bool = false + if res := C.icsneoc2_message_is_transmit(device, message, &isTransmit); res != C.icsneoc2_error_success { + printError(res) + return false + } + if isTransmit { + txCount += 1 + continue + } + fmt.Printf("\t%d) Message type: %d bus type: %s (%d)\n", i, msgType, msgBusTypeName, msgBusType) + if msgBusType == C.icsneoc2_msg_bus_type_can { + var arbid C.uint32_t = 0 + var dlc C.int32_t = 0 + var netid C.icsneoc2_netid_t = 0 + var isRemote C.bool = false + var isCanfd C.bool = false + var isExtended C.bool = false + data := make([]byte, 64) + var dataLength C.uint32_t = 64 + netidName := make([]byte, 128) + var netidNameLength C.uint32_t = 128 + var res C.icsneoc2_error_t = C.icsneoc2_error_success + res = C.icsneoc2_message_netid_get(device, message, &netid) + res += C.icsneoc2_netid_name_get(netid, (*C.char)(unsafe.Pointer(&netidName)), &netidNameLength) + res += C.icsneoc2_message_can_arbid_get(device, message, &arbid) + res += C.icsneoc2_message_can_dlc_get(device, message, &dlc) + res += C.icsneoc2_message_can_is_remote(device, message, &isRemote) + res += C.icsneoc2_message_can_is_canfd(device, message, &isCanfd) + res += C.icsneoc2_message_can_is_extended(device, message, &isExtended) + res += C.icsneoc2_message_data_get(device, message, (*C.uint8_t)(unsafe.Pointer(&data[0])), &dataLength) + // We really should check the error message for all of these since we can't tell the exact error if something + // bad happens but for an example this should be okay. + if res != C.icsneoc2_error_success { + fmt.Printf("\tFailed to get CAN parameters (error: %d) for index %d\n", res, i) + continue + } + // Finally lets print the RX message + fmt.Printf("\t NetID: %s (0x%X)\tArbID: 0x%X\t DLC: %d\t Remote: %t\t CANFD: %t\t Extended: %t\t Data length: %d\n", netidName, netid, arbid, dlc, isRemote, isCanfd, isExtended, dataLength) + fmt.Printf("\t Data: [") + for _, d := range data[:dataLength] { + fmt.Printf(" 0x%X", d) + } + println(" ]") + continue + } else { + fmt.Printf("\tIgnoring bus message type: %d (%s)\n", msgBusType, msgBusTypeName) + continue + } + + } + fmt.Printf("\tReceived %d messages total, %d were TX messages\n", len(messages), txCount) + return true +} diff --git a/examples/zig/simple/.gitignore b/examples/zig/simple/.gitignore new file mode 100644 index 0000000..8e179b9 --- /dev/null +++ b/examples/zig/simple/.gitignore @@ -0,0 +1,22 @@ +# This file is for zig-specific build artifacts. +# If you have OS-specific or editor-specific files to ignore, +# such as *.swp or .DS_Store, put those in your global +# ~/.gitignore and put this in your ~/.gitconfig: +# +# [core] +# excludesfile = ~/.gitignore +# +# Cheers! +# -andrewrk + +.zig-cache/ +zig-out/ +/release/ +/debug/ +/build/ +/build-*/ +/docgen_tmp/ + +# Although this was renamed to .zig-cache, let's leave it here for a few +# releases to make it less annoying to work with multiple branches. +zig-cache/ \ No newline at end of file diff --git a/examples/zig/simple/build.zig b/examples/zig/simple/build.zig new file mode 100644 index 0000000..3cadb0c --- /dev/null +++ b/examples/zig/simple/build.zig @@ -0,0 +1,72 @@ +const std = @import("std"); + +// Although this function looks imperative, note that its job is to +// declaratively construct a build graph that will be executed by an external +// runner. +pub fn build(b: *std.Build) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{ .default_target = .{ .abi = .msvc } }); + + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "simple", + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + exe.linkLibC(); + // Add support for icsneoc2 + exe.addIncludePath(b.path("../../../include")); + exe.addLibraryPath(b.path("../../../build")); + exe.linkSystemLibrary("icsneoc2"); + + // This declares intent for the executable to be installed into the + // standard location when the user invokes the "install" step (the default + // step when running `zig build`). + b.installArtifact(exe); + + // This *creates* a Run step in the build graph, to be executed when another + // step is evaluated that depends on it. The next line below will establish + // such a dependency. + const run_cmd = b.addRunArtifact(exe); + + // By making the run step depend on the install step, it will be run from the + // installation directory rather than directly from within the cache directory. + // This is not necessary, however, if the application depends on other installed + // files, this ensures they will be present and in the expected location. + run_cmd.step.dependOn(b.getInstallStep()); + + // This allows the user to pass arguments to the application in the build + // command itself, like this: `zig build run -- arg1 arg2 etc` + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build run` + // This will evaluate the `run` step rather than the default, which is "install". + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const exe_unit_tests = b.addTest(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }); + + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + + // Similar to creating the run step earlier, this exposes a `test` step to + // the `zig build --help` menu, providing a way for the user to request + // running the unit tests. + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_exe_unit_tests.step); +} diff --git a/examples/zig/simple/build.zig.zon b/examples/zig/simple/build.zig.zon new file mode 100644 index 0000000..4640a95 --- /dev/null +++ b/examples/zig/simple/build.zig.zon @@ -0,0 +1,72 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = "simple", + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + // + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/examples/zig/simple/src/main.zig b/examples/zig/simple/src/main.zig new file mode 100644 index 0000000..cb5af51 --- /dev/null +++ b/examples/zig/simple/src/main.zig @@ -0,0 +1,421 @@ +const std = @import("std"); +const print = std.debug.print; + +const ics = @cImport({ + @cInclude("icsneo/icsneoc2.h"); + @cInclude("icsneo/icsneoc2types.h"); +}); + +const c = @cImport({ + @cInclude("time.h"); +}); + +pub fn main() !void { + // Find devices connected to host. + const MAX_DEVICE_COUNT: u32 = 255; + var device_buffer: [MAX_DEVICE_COUNT]?*ics.icsneoc2_device_t = undefined; + var devices_count: u32 = MAX_DEVICE_COUNT; + print("Finding devices... ", .{}); + if (!check_error( + ics.icsneoc2_device_find_all(&device_buffer, &devices_count, null), + "Failed to find device", + )) { + return; + } + print("OK, {d} device{s} found\n", .{ devices_count, if (devices_count == 1) "" else "s" }); + // Lets just take a slice of the entire device buffer + const devices = device_buffer[0..devices_count]; + // List off the devices + for (devices) |device| { + // Get description of the device + var description: [255]u8 = [_:0]u8{0} ** 255; + var description_length: u32 = 255; + if (!check_error( + ics.icsneoc2_device_description_get(device, &description, &description_length), + "\tFailed to get device description", + )) { + return; + } + print("{s}. {*}\n", .{ description, device.? }); + // Get/Set open options + var options: ics.icsneoc2_open_options_t = ics.icsneoc2_open_options_none; + if (!check_error( + ics.icsneoc2_device_open_options_get(device, &options), + "\tFailed to get device open options", + )) { + return; + } + // Disable Syncing RTC and going online + options &= ~@as(ics.icsneoc2_open_options_t, ics.icsneoc2_open_options_sync_rtc); + options &= ~@as(ics.icsneoc2_open_options_t, ics.icsneoc2_open_options_go_online); + print("\tDevice open options: 0x{X}\n", .{options}); + if (!check_error( + ics.icsneoc2_device_open_options_set(device, options), + "\tFailed to set device open options", + )) { + return; + } + // Open the device + print("\tOpening device: {s}...\n", .{description}); + if (!check_error( + ics.icsneoc2_device_open(device), + "\tFailed to open device", + )) { + return; + } + defer { + // Finally, close the device. + if (!print_device_events(device)) { + print("\tFailed to print events...\n", .{}); + } + print("\tClosing device: {s}... ", .{description}); + if (check_error( + ics.icsneoc2_device_close(device), + "\tFailed to close device", + )) { + print("OK\n", .{}); + } + } + // Get timestamp resolution of the device + print("\tGetting timestamp resolution... ", .{}); + var timestamp_resolution: u32 = 0; + if (!check_error( + ics.icsneoc2_device_timestamp_resolution_get(device, ×tamp_resolution), + "\tFailed to get timestamp resolution", + )) { + return; + } + print("{d}ns\n", .{timestamp_resolution}); + // Get baudrates for HSCAN + print("\tGetting HSCAN baudrate... ", .{}); + var baudrate: u64 = 0; + if (!check_error( + ics.icsneoc2_device_baudrate_get(device, ics.icsneoc2_netid_hscan, &baudrate), + "\tFailed to get baudrate", + )) { + return; + } + print("{d}mbit/s\n", .{baudrate}); + // Get FDbaudrates for HSCAN + print("\tGetting FD HSCAN baudrate... ", .{}); + var fd_baudrate: u64 = 0; + if (!check_error( + ics.icsneoc2_device_canfd_baudrate_get(device, ics.icsneoc2_netid_hscan, &fd_baudrate), + "\tFailed to get FD baudrate", + )) { + return; + } + print("{d}mbit/s\n", .{fd_baudrate}); + // Set baudrates for HSCAN + // save_to_device: If this is set to true, the baudrate will be saved on the device + // and will persist through a power cycle + print("\tSetting HSCAN Baudrate... ", .{}); + const save_to_device: bool = false; + if (!check_error( + ics.icsneoc2_device_baudrate_set(device, ics.icsneoc2_netid_hscan, baudrate, save_to_device), + "\tFailed to set baudrate", + )) { + return; + } + print("OK\n", .{}); + // Set FDbaudrates for HSCAN + print("\tSetting FD HSCAN Baudrate... ", .{}); + if (!check_error( + ics.icsneoc2_device_canfd_baudrate_set(device, ics.icsneoc2_netid_hscan, baudrate, save_to_device), + "\tFailed to set FD baudrate", + )) { + return; + } + print("OK\n", .{}); + // Get RTC + print("\tGetting RTC... ", .{}); + var unix_epoch: c.time_t = 0; + if (!check_error( + ics.icsneoc2_device_rtc_get(device, &unix_epoch), + "\tFailed to get RTC", + )) { + return; + } + print_rtc(unix_epoch); + // Set RTC + print("\tSetting RTC to current time... ", .{}); + const current_time: i64 = c.time(0); + if (!check_error( + ics.icsneoc2_device_rtc_set(device, current_time), + "\tFailed to set RTC", + )) { + return; + } + print("OK\n", .{}); + // Get RTC + print("\tGetting RTC... ", .{}); + if (!check_error( + ics.icsneoc2_device_rtc_get(device, &unix_epoch), + "\tFailed to get RTC", + )) { + return; + } + print_rtc(unix_epoch); + // Go online, start acking traffic + print("\tGoing online... ", .{}); + if (!check_error( + ics.icsneoc2_device_go_online(device, true), + "\tFailed to go online", + )) { + return; + } + // Redundant check to show how to check if the device is online, if the previous + // icsneoc2_device_go_online call was successful we can assume we are online already + var is_online: bool = false; + if (!check_error( + ics.icsneoc2_device_is_online(device, &is_online), + "\tFailed to check if online", + )) { + return; + } + print("{s}\n", .{if (is_online) "Online" else "Offline"}); + // Transmit CAN messages + if (!transmit_can_messages(device)) { + return; + } + // Wait for the bus to collect some messages, requires an active bus to get messages + print("\tWaiting 1 second for messages...\n", .{}); + std.time.sleep(std.time.ns_per_s); + // Get the messages + var messages: [20000]?*ics.icsneoc2_message_t = [_]?*ics.icsneoc2_message_t{null} ** 20000; + var messages_count: u32 = 20000; + const res = ics.icsneoc2_device_messages_get(device, &messages, &messages_count, 3000); + if (!check_error( + res, + "\tFailed to get messages on device", + )) { + return; + } + // Process the messages + if (!process_messages(device, messages[0..messages_count])) { + return; + } + } +} + +pub fn check_error(error_code: ics.icsneoc2_error_t, error_msg: []const u8) bool { + if (error_code == ics.icsneoc2_error_success) { + return true; + } + var error_str: [256]u8 = [_:0]u8{0} ** 256; + var error_length: u32 = 256; + const res: ics.icsneoc2_error_t = ics.icsneoc2_error_code_get( + error_code, + &error_str, + &error_length, + ); + if (res != ics.icsneoc2_error_success) { + print( + "{s}: Failed to get string for error code {d} with error code {d}\n", + .{ error_msg, error_code, res }, + ); + return false; + } + print( + "{s}: \"{s}\" ({d})\n", + .{ error_msg, error_str, error_code }, + ); + return error_code == ics.icsneoc2_error_success; +} + +pub fn print_rtc(unix_epoch: c.time_t) void { + var rtc_time: [32]u8 = [_:0]u8{0} ** 32; + _ = c.strftime(&rtc_time, rtc_time.len, "%Y-%m-%d %H:%M:%S", c.localtime(&unix_epoch)); + print("{d} {s}\n", .{ unix_epoch, rtc_time }); +} + +pub fn transmit_can_messages(device: ?*ics.icsneoc2_device_t) bool { + const msg_count: usize = 100; + print("\tTransmitting {} messages...\n", .{msg_count}); + for (0..msg_count) |counter| { + // Create the message + var message: ?*ics.icsneoc2_message_t = null; + var message_count: u32 = 1; + if (!check_error( + ics.icsneoc2_message_can_create(device, &message, message_count), + "\tFailed to create CAN message", + )) { + return false; + } + defer { + _ = check_error( + ics.icsneoc2_message_can_free(device, message), + "\tFailed to free CAN message", + ); + } + // Set the message attributes + var res: ics.icsneoc2_error_t = ics.icsneoc2_message_netid_set(device, message, ics.icsneoc2_netid_hscan); + res += ics.icsneoc2_message_can_arbid_set(device, message, 0x10); + res += ics.icsneoc2_message_can_canfd_set(device, message, true); + res += ics.icsneoc2_message_can_extended_set(device, message, true); + res += ics.icsneoc2_message_can_baudrate_switch_set(device, message, true); + // Create the payload + var data: [8]u8 = .{ + @intCast(counter >> 56), + @intCast(counter >> 48), + @intCast(counter >> 40), + @intCast(counter >> 32), + @intCast(counter >> 24), + @intCast(counter >> 16), + @intCast(counter >> 8), + @intCast(counter >> 0), + }; + res += ics.icsneoc2_message_data_set(device, message, &data, data.len); + res += ics.icsneoc2_message_can_dlc_set(device, message, -1); + if (!check_error(res, "\tFailed to set CAN Message attributes!")) { + return false; + } + + if (!check_error( + ics.icsneoc2_device_messages_transmit(device, &message, &message_count), + "\tFailed to transmit message", + )) { + return false; + } + } + return true; +} + +pub fn process_messages(device: ?*ics.icsneoc2_device_t, messages: []const ?*ics.icsneoc2_message_t) bool { + var tx_count: usize = 0; + for (messages, 0..) |message, i| { + // Get the message type + var msg_type: ics.icsneoc2_msg_type_t = 0; + if (!check_error( + ics.icsneoc2_message_type_get(device, message.?, &msg_type), + "\tFailed to get message type", + )) { + return false; + } + // Get the message type name + var msg_type_name: [128]u8 = [_:0]u8{0} ** 128; + var msg_type_name_length: u32 = 128; + if (!check_error( + ics.icsneoc2_message_type_name_get(msg_type, &msg_type_name, &msg_type_name_length), + "\tFailed to get message type name", + )) { + return false; + } + // Check if the message is a bus message, ignore otherwise + if (msg_type != ics.icsneoc2_msg_type_bus) { + print("\tIgnoring message type: {d} ({s})\n", .{ msg_type, msg_type_name }); + continue; + } + // Get the message bus type + var msg_bus_type: ics.icsneoc2_msg_bus_type_t = 0; + if (!check_error( + ics.icsneoc2_message_bus_type_get(device, message, &msg_bus_type), + "\tFailed to get message bus type", + )) { + return false; + } + // Get the message type name + var msg_bus_type_name: [128]u8 = [_:0]u8{0} ** 128; + var msg_bus_type_name_length: u32 = 128; + if (!check_error( + ics.icsneoc2_bus_type_name_get(msg_bus_type, &msg_bus_type_name, &msg_bus_type_name_length), + "\tFailed to get message bus type name", + )) { + return false; + } + // Check if message is a transmit message + var is_tx: bool = false; + if (!check_error( + ics.icsneoc2_message_is_transmit(device, message, &is_tx), + "\tFailed to get message is transmit", + )) { + return false; + } + if (is_tx) { + tx_count += 1; + continue; + } + print("\t{d} Message type: {d} bus type: {s} ({d})\n", .{ i, msg_type, msg_bus_type_name, msg_bus_type }); + // Check if the message is a CAN message, ignore otherwise + if (msg_bus_type == ics.icsneoc2_msg_bus_type_can) { + var arbid: u32 = 0; + var dlc: i32 = 0; + var netid: ics.icsneoc2_netid_t = 0; + var is_remote: bool = false; + var is_canfd: bool = false; + var is_extended: bool = false; + var data: [64]u8 = [_]u8{0} ** 64; + var data_length: u32 = 64; + var netid_name: [128]u8 = [_:0]u8{0} ** 128; + var netid_name_length: u32 = 128; + var res: ics.icsneoc2_error_t = ics.icsneoc2_error_success; + res = ics.icsneoc2_message_netid_get(device, message, &netid); + res += ics.icsneoc2_netid_name_get(netid, &netid_name, &netid_name_length); + res += ics.icsneoc2_message_can_arbid_get(device, message, &arbid); + res += ics.icsneoc2_message_can_dlc_get(device, message, &dlc); + res += ics.icsneoc2_message_can_is_remote(device, message, &is_remote); + res += ics.icsneoc2_message_can_is_canfd(device, message, &is_canfd); + res += ics.icsneoc2_message_can_is_extended(device, message, &is_extended); + res += ics.icsneoc2_message_data_get(device, message, &data, &data_length); + // We really should check the error message for all of these since we can't tell the exact error if something + // bad happens but for an example this should be okay. + if (res != ics.icsneoc2_error_success) { + print("\tFailed to get CAN parameters (error: {d}) for index {d}\n", .{ res, i }); + continue; + } + // Finally lets print the RX message + print("\t NetID: {s} (0x{X})\tArbID: 0x{X}\t DLC: {d}\t Remote: {}\t CANFD: {}\t Extended: {}\t Data length: {d}\n", .{ netid_name, netid, arbid, dlc, is_remote, is_canfd, is_extended, data_length }); + print("\t Data: {any}\n", .{data[0..data_length]}); + } else { + print("\tIgnoring bus message type: {d} ({s})\n", .{ msg_bus_type, msg_bus_type_name }); + continue; + } + } + print("\tReceived {d} messages total, {d} were TX messages\n", .{ messages.len, tx_count }); + return true; +} + +pub fn print_device_events(device: ?*ics.icsneoc2_device_t) bool { + // Get device events + var events: [1024]?*ics.icsneoc2_event_t = [_]?*ics.icsneoc2_event_t{null} ** 1024; + var events_count: u32 = 1024; + if (!check_error( + ics.icsneoc2_device_events_get(device, &events, &events_count), + "\tFailed to get device events", + )) { + return false; + } + for (events[0..events_count], 0..) |event, i| { + var event_description: [256]u8 = [_:0]u8{0} ** 256; + var event_description_length: u32 = 256; + if (!check_error( + ics.icsneoc2_event_description_get(event, &event_description, &event_description_length), + "\tFailed to get event description", + )) { + continue; + } + print("\tEvent {d}: {s}\n", .{ i, event_description }); + } + // Get global events + var global_events: [1024]?*ics.icsneoc2_event_t = [_]?*ics.icsneoc2_event_t{null} ** 1024; + var global_events_count: u32 = 1024; + if (!check_error( + ics.icsneoc2_events_get(&global_events, &global_events_count), + "\tFailed to get device global events", + )) { + return false; + } + for (global_events[0..global_events_count], 0..) |event, i| { + var event_description: [256]u8 = [_:0]u8{0} ** 256; + var event_description_length: u32 = 256; + if (!check_error( + ics.icsneoc2_event_description_get(event, &event_description, &event_description_length), + "\tFailed to get event description", + )) { + continue; + } + print("\tGlobal event {d}: {s}\n", .{ i, event_description }); + } + print("\tReceived {d} events and {d} global events\n", .{ events_count, global_events_count }); + return true; +} diff --git a/include/icsneo/communication/message/a2bmessage.h b/include/icsneo/communication/message/a2bmessage.h index 62e3562..79278df 100644 --- a/include/icsneo/communication/message/a2bmessage.h +++ b/include/icsneo/communication/message/a2bmessage.h @@ -20,8 +20,10 @@ enum class PCMType : uint8_t { using ChannelMap = std::unordered_map; -class A2BMessage : public Frame { +class A2BMessage : public BusMessage { public: + const BusMessage::Type getBusType() const final { return BusMessage::Type::A2B; } + static constexpr size_t maxAudioBufferSize = 2048; enum class TDMMode : uint8_t { diff --git a/include/icsneo/communication/message/apperrormessage.h b/include/icsneo/communication/message/apperrormessage.h index 8c81b54..e3dbc62 100644 --- a/include/icsneo/communication/message/apperrormessage.h +++ b/include/icsneo/communication/message/apperrormessage.h @@ -64,9 +64,9 @@ enum class AppErrorType : uint16_t { AppNoError = 255 }; -class AppErrorMessage : public RawMessage { +class AppErrorMessage : public InternalMessage { public: - AppErrorMessage() : RawMessage(Message::Type::AppError, Network::NetID::RED_App_Error) {} + AppErrorMessage() : InternalMessage(Message::Type::AppError, Network::NetID::RED_App_Error) {} uint16_t errorType; Network::NetID errorNetID; uint32_t timestamp10us; diff --git a/include/icsneo/communication/message/canmessage.h b/include/icsneo/communication/message/canmessage.h index 76338fe..a945946 100644 --- a/include/icsneo/communication/message/canmessage.h +++ b/include/icsneo/communication/message/canmessage.h @@ -7,8 +7,10 @@ namespace icsneo { -class CANMessage : public Frame { +class CANMessage : public BusMessage { public: + const BusMessage::Type getBusType() const final { return BusMessage::Type::CAN; } + uint32_t arbid; uint8_t dlcOnWire; bool isRemote = false; // Not allowed if CAN FD diff --git a/include/icsneo/communication/message/diskdatamessage.h b/include/icsneo/communication/message/diskdatamessage.h index 6681c5a..899f1a8 100644 --- a/include/icsneo/communication/message/diskdatamessage.h +++ b/include/icsneo/communication/message/diskdatamessage.h @@ -7,9 +7,9 @@ namespace icsneo { -class DiskDataMessage : public RawMessage { +class DiskDataMessage : public InternalMessage { public: - DiskDataMessage(std::vector&& d) : RawMessage(Network::NetID::DiskData) { + DiskDataMessage(std::vector&& d) : InternalMessage(Network::NetID::DiskData) { data = std::move(d); } }; diff --git a/include/icsneo/communication/message/ethernetmessage.h b/include/icsneo/communication/message/ethernetmessage.h index d71d2d4..2375079 100644 --- a/include/icsneo/communication/message/ethernetmessage.h +++ b/include/icsneo/communication/message/ethernetmessage.h @@ -31,8 +31,10 @@ struct MACAddress { } }; -class EthernetMessage : public Frame { +class EthernetMessage : public BusMessage { public: + const BusMessage::Type getBusType() const final { return BusMessage::Type::Ethernet; } + bool preemptionEnabled = false; uint8_t preemptionFlags = 0; std::optional fcs; diff --git a/include/icsneo/communication/message/extendeddatamessage.h b/include/icsneo/communication/message/extendeddatamessage.h index 7d65228..7fdee1e 100644 --- a/include/icsneo/communication/message/extendeddatamessage.h +++ b/include/icsneo/communication/message/extendeddatamessage.h @@ -9,7 +9,7 @@ namespace icsneo { -class ExtendedDataMessage : public Frame { +class ExtendedDataMessage : public InternalMessage { public: #pragma pack(push, 2) struct ExtendedDataHeader { diff --git a/include/icsneo/communication/message/filter/canmessagefilter.h b/include/icsneo/communication/message/filter/canmessagefilter.h index 7bc5941..2d399ec 100644 --- a/include/icsneo/communication/message/filter/canmessagefilter.h +++ b/include/icsneo/communication/message/filter/canmessagefilter.h @@ -13,8 +13,8 @@ namespace icsneo { class CANMessageFilter : public MessageFilter { public: - CANMessageFilter() : MessageFilter(Network::Type::CAN), arbid(INVALID_ARBID) { messageType = Message::Type::Frame; } - CANMessageFilter(uint32_t arbid) : MessageFilter(Network::Type::CAN), arbid(arbid) { messageType = Message::Type::Frame; } + CANMessageFilter() : MessageFilter(Network::Type::CAN), arbid(INVALID_ARBID) { messageType = Message::Type::BusMessage; } + CANMessageFilter(uint32_t arbid) : MessageFilter(Network::Type::CAN), arbid(arbid) { messageType = Message::Type::BusMessage; } bool match(const std::shared_ptr& message) const { if(!MessageFilter::match(message)) diff --git a/include/icsneo/communication/message/filter/messagefilter.h b/include/icsneo/communication/message/filter/messagefilter.h index 94d5892..d44fb88 100644 --- a/include/icsneo/communication/message/filter/messagefilter.h +++ b/include/icsneo/communication/message/filter/messagefilter.h @@ -27,9 +27,9 @@ public: if(!matchMessageType(message->type)) return false; - if(message->type == Message::Type::Frame || message->type == Message::Type::Main51 || - message->type == Message::Type::RawMessage || message->type == Message::Type::ReadSettings) { - const auto frame = std::static_pointer_cast(message); + if(message->type == Message::Type::BusMessage || message->type == Message::Type::Main51 || + message->type == Message::Type::InternalMessage || message->type == Message::Type::ReadSettings) { + const auto frame = std::static_pointer_cast(message); if(!matchNetworkType(frame->network.getType())) return false; if(!matchNetID(frame->network.getNetID())) diff --git a/include/icsneo/communication/message/flashmemorymessage.h b/include/icsneo/communication/message/flashmemorymessage.h index 4ac2e6b..175345f 100644 --- a/include/icsneo/communication/message/flashmemorymessage.h +++ b/include/icsneo/communication/message/flashmemorymessage.h @@ -7,9 +7,9 @@ namespace icsneo { -class FlashMemoryMessage : public RawMessage { +class FlashMemoryMessage : public InternalMessage { public: - FlashMemoryMessage() : RawMessage(Message::Type::RawMessage, Network::NetID::RED_INT_MEMORYREAD) {} + FlashMemoryMessage() : InternalMessage(Message::Type::InternalMessage, Network::NetID::RED_INT_MEMORYREAD) {} uint16_t startAddress = 0; }; diff --git a/include/icsneo/communication/message/flexray/flexraymessage.h b/include/icsneo/communication/message/flexray/flexraymessage.h index 16f5907..b4565e0 100644 --- a/include/icsneo/communication/message/flexray/flexraymessage.h +++ b/include/icsneo/communication/message/flexray/flexraymessage.h @@ -10,8 +10,10 @@ namespace icsneo { -class FlexRayMessage : public Frame { +class FlexRayMessage : public BusMessage { public: + const BusMessage::Type getBusType() const final { return BusMessage::Type::FlexRay; } + uint16_t slotid = 0; double tsslen = 0; double framelen = 0; diff --git a/include/icsneo/communication/message/i2cmessage.h b/include/icsneo/communication/message/i2cmessage.h index 86c3058..20f6bbc 100644 --- a/include/icsneo/communication/message/i2cmessage.h +++ b/include/icsneo/communication/message/i2cmessage.h @@ -8,8 +8,10 @@ namespace icsneo { -class I2CMessage : public Frame { +class I2CMessage : public BusMessage { public: + const BusMessage::Type getBusType() const final { return BusMessage::Type::I2C; } + enum class DeviceMode : uint8_t { Target = 0, Controller = 1 diff --git a/include/icsneo/communication/message/iso9141message.h b/include/icsneo/communication/message/iso9141message.h index 70b85ba..0db388c 100644 --- a/include/icsneo/communication/message/iso9141message.h +++ b/include/icsneo/communication/message/iso9141message.h @@ -8,8 +8,10 @@ namespace icsneo { -class ISO9141Message : public Frame { +class ISO9141Message : public BusMessage { public: + const BusMessage::Type getBusType() const final { return BusMessage::Type::ISO9141; } + std::array header; bool isInit = false; bool isBreak = false; diff --git a/include/icsneo/communication/message/linmessage.h b/include/icsneo/communication/message/linmessage.h index 683fc1e..d2f2d31 100644 --- a/include/icsneo/communication/message/linmessage.h +++ b/include/icsneo/communication/message/linmessage.h @@ -34,8 +34,10 @@ struct LINStatusFlags { bool BreakOnly = false; }; -class LINMessage : public Frame { +class LINMessage : public BusMessage { public: + const BusMessage::Type getBusType() const final { return BusMessage::Type::LIN; } + enum class Type : uint8_t { NOT_SET = 0, LIN_COMMANDER_MSG, diff --git a/include/icsneo/communication/message/livedatamessage.h b/include/icsneo/communication/message/livedatamessage.h index 853e761..87554a9 100644 --- a/include/icsneo/communication/message/livedatamessage.h +++ b/include/icsneo/communication/message/livedatamessage.h @@ -8,9 +8,9 @@ #include "icsneo/communication/livedata.h" namespace icsneo { -class LiveDataMessage : public RawMessage { +class LiveDataMessage : public InternalMessage { public: - LiveDataMessage() : RawMessage(Message::Type::LiveData, Network::NetID::ExtendedCommand) {} + LiveDataMessage() : InternalMessage(Message::Type::LiveData, Network::NetID::ExtendedCommand) {} LiveDataHandle handle; LiveDataCommand cmd; }; diff --git a/include/icsneo/communication/message/main51message.h b/include/icsneo/communication/message/main51message.h index 12f51ea..1f2d288 100644 --- a/include/icsneo/communication/message/main51message.h +++ b/include/icsneo/communication/message/main51message.h @@ -8,9 +8,9 @@ namespace icsneo { -class Main51Message : public RawMessage { +class Main51Message : public InternalMessage { public: - Main51Message() : RawMessage(Message::Type::Main51, Network::NetID::Main51) {} + Main51Message() : InternalMessage(Message::Type::Main51, Network::NetID::Main51) {} Command command = Command(0); bool forceShortFormat = false; // Necessary for EnableNetworkCom and EnableNetworkComEx }; diff --git a/include/icsneo/communication/message/mdiomessage.h b/include/icsneo/communication/message/mdiomessage.h index 92960bc..78df2b5 100644 --- a/include/icsneo/communication/message/mdiomessage.h +++ b/include/icsneo/communication/message/mdiomessage.h @@ -7,8 +7,10 @@ namespace icsneo { -class MDIOMessage : public Frame { +class MDIOMessage : public BusMessage { public: + const BusMessage::Type getBusType() const final { return BusMessage::Type::MDIO; } + enum class Clause : uint8_t { Clause45 = 0, Clause22 = 1 diff --git a/include/icsneo/communication/message/message.h b/include/icsneo/communication/message/message.h index 27f79c5..e3e0bb8 100644 --- a/include/icsneo/communication/message/message.h +++ b/include/icsneo/communication/message/message.h @@ -7,14 +7,70 @@ typedef uint16_t neomessagetype_t; #ifdef __cplusplus #include "icsneo/communication/network.h" +#include "icsneo/icsneoc2types.h" #include +#include +#include namespace icsneo { -class Message { +/** + * @brief Type of message class + * + * @see AbstractMessage::getMsgType() + */ +typedef enum class MessageType : icsneoc2_msg_type_t { + Device = icsneoc2_msg_type_device, + Internal = icsneoc2_msg_type_internal, + Bus = icsneoc2_msg_type_bus, + + MaxSize = icsneoc2_msg_type_maxsize, +} MessageType; + +/** + * @brief Pure virtual abstract class for all messages. Inherit from Message over this class. + * + * @see Message + * + */ +class AbstractMessage { public: + virtual const MessageType getMsgType() const = 0; +}; + +/** + * @brief Base Message class representing Device messages. + */ +class Message : public AbstractMessage { +public: + virtual const MessageType getMsgType() const { return MessageType::Device; } + + /** + * @brief Get the string representation of the message type + * + * @return String representation of the message type + * + * @see AbstractMessage::getMsgType() + */ + static std::string getMsgTypeName(MessageType msgType) { + switch (msgType) { + case MessageType::Device: + return "Device"; + case MessageType::Internal: + return "Internal"; + case MessageType::Bus: + return "Bus"; + // Don't default here so we can rely on the compiler to warn us about missing cases + }; + std::stringstream ss; + ss << "Unknown (" << static_cast(msgType) << ")"; + return ss.str(); + } + enum class Type : neomessagetype_t { - Frame = 0, + BusMessage = 0, + // Deprecated: Will be removed in the future. + Frame = BusMessage, CANErrorCount = 0x100, CANError = 0x100, @@ -24,7 +80,9 @@ public: // Past 0x8000 are all for internal use only Invalid = 0x8000, - RawMessage = 0x8001, + InternalMessage = 0x8001, + // Deprecated: Will be removed in the future. + RawMessage = InternalMessage, ReadSettings = 0x8002, ResetStatus = 0x8003, DeviceVersion = 0x8004, @@ -52,26 +110,69 @@ public: uint64_t timestamp = 0; }; -class RawMessage : public Message { +/** + * @brief Internal Message class representing Device messages that shouldn't be exposed to public APIs. + */ +class InternalMessage : public Message { public: - RawMessage(Message::Type type = Message::Type::RawMessage) : Message(type) {} - RawMessage(Message::Type type, Network net) : Message(type), network(net) {} - RawMessage(Network net) : Message(Message::Type::RawMessage), network(net) {} - RawMessage(Network net, std::vector d) : Message(Message::Type::RawMessage), network(net), data(d) {} + InternalMessage(Message::Type type = Message::Type::InternalMessage) : Message(type) {} + InternalMessage(Message::Type type, Network net) : Message(type), network(net) {} + InternalMessage(Network net) : Message(Message::Type::InternalMessage), network(net) {} + InternalMessage(Network net, std::vector d) : Message(Message::Type::InternalMessage), network(net), data(d) {} + + virtual const MessageType getMsgType() const { return MessageType::Internal; } Network network; std::vector data; }; -class Frame : public RawMessage { +/** + * @brief Bus Message class representing Device messages representing Bus networks like CAN, LIN, Ethernet, etc. + */ +class BusMessage : public InternalMessage { public: - Frame() : RawMessage(Message::Type::Frame) {} + BusMessage() : InternalMessage(Message::Type::BusMessage) {} + /** @brief + * Bus message types, useful for filtering out or identifying Bus Messages. + */ + typedef enum class Type : icsneoc2_msg_bus_type_t { + Invalid = icsneoc2_msg_bus_type_invalid, + Internal = icsneoc2_msg_bus_type_internal, + CAN = icsneoc2_msg_bus_type_can, + LIN = icsneoc2_msg_bus_type_lin, + FlexRay = icsneoc2_msg_bus_type_flexray, + MOST = icsneoc2_msg_bus_type_most, + Ethernet = icsneoc2_msg_bus_type_ethernet, + LSFTCAN = icsneoc2_msg_bus_type_lsftcan, + SWCAN = icsneoc2_msg_bus_type_swcan, + ISO9141 = icsneoc2_msg_bus_type_iso9141, + I2C = icsneoc2_msg_bus_type_i2c, + A2B = icsneoc2_msg_bus_type_a2b, + SPI = icsneoc2_msg_bus_type_spi, + MDIO = icsneoc2_msg_bus_type_mdio, + ANY = icsneoc2_msg_bus_type_any, + OTHER = icsneoc2_msg_bus_type_other + } Type; + + const MessageType getMsgType() const final { return MessageType::Bus; } + + virtual const BusMessage::Type getBusType() const = 0; + + // Description ID of the message. This is used for filtering / tracking in firmware and driver. + // This is equivalent to icsSpyMessage::DescriptionID uint16_t description = 0; + // weather the message was originally transmitted on the bus. This is equivalent to + // SPY_STATUS_TX_MSG bit field in icsSpyMessage::StatusBitField bool transmitted = false; bool error = false; }; +/** @brief Backwards compatibility, RawMessage was renamed to better reflect what it actually does. */ +typedef InternalMessage RawMessage; +/** @brief Backwards compatibility, Frame was renamed to better reflect what it actually does. */ +typedef BusMessage Frame; + } #endif // __cplusplus diff --git a/include/icsneo/communication/message/neoreadmemorysdmessage.h b/include/icsneo/communication/message/neoreadmemorysdmessage.h index bf9bfcc..008069f 100644 --- a/include/icsneo/communication/message/neoreadmemorysdmessage.h +++ b/include/icsneo/communication/message/neoreadmemorysdmessage.h @@ -7,9 +7,9 @@ namespace icsneo { -class NeoReadMemorySDMessage : public RawMessage { +class NeoReadMemorySDMessage : public InternalMessage { public: - NeoReadMemorySDMessage() : RawMessage(Message::Type::RawMessage, Network::NetID::NeoMemorySDRead) {} + NeoReadMemorySDMessage() : InternalMessage(Message::Type::InternalMessage, Network::NetID::NeoMemorySDRead) {} uint32_t startAddress = 0; }; diff --git a/include/icsneo/communication/message/readsettingsmessage.h b/include/icsneo/communication/message/readsettingsmessage.h index 7c05ecf..d2625ca 100644 --- a/include/icsneo/communication/message/readsettingsmessage.h +++ b/include/icsneo/communication/message/readsettingsmessage.h @@ -8,9 +8,9 @@ namespace icsneo { -class ReadSettingsMessage : public RawMessage { +class ReadSettingsMessage : public InternalMessage { public: - ReadSettingsMessage() : RawMessage(Message::Type::ReadSettings, Network::NetID::ReadSettings) {} + ReadSettingsMessage() : InternalMessage(Message::Type::ReadSettings, Network::NetID::ReadSettings) {} enum class Response : uint8_t { OK = 0, diff --git a/include/icsneo/communication/network.h b/include/icsneo/communication/network.h index 2c4a11d..2c904e6 100644 --- a/include/icsneo/communication/network.h +++ b/include/icsneo/communication/network.h @@ -12,6 +12,8 @@ typedef uint8_t neonettype_t; #include #include +#include + namespace icsneo { class Network { @@ -23,187 +25,187 @@ class Network { static constexpr uint16_t OFFSET_PLASMA_SLAVE3_RANGE2 = 12800; public: - enum class NetID : neonetid_t { - Device = 0, - HSCAN = 1, - MSCAN = 2, - SWCAN = 3, - LSFTCAN = 4, - FordSCP = 5, - J1708 = 6, - Aux = 7, - J1850VPW = 8, - ISO9141 = 9, - DiskData = 10, - Main51 = 11, - RED = 12, - SCI = 13, - ISO9141_2 = 14, - ISO14230 = 15, - LIN = 16, - OP_Ethernet1 = 17, - OP_Ethernet2 = 18, - OP_Ethernet3 = 19, + enum class NetID : icsneoc2_netid_t { + Device = icsneoc2_netid_device, + HSCAN = icsneoc2_netid_hscan, + MSCAN = icsneoc2_netid_mscan, + SWCAN = icsneoc2_netid_swcan, + LSFTCAN = icsneoc2_netid_lsftcan, + FordSCP = icsneoc2_netid_fordscp, + J1708 = icsneoc2_netid_j1708, + Aux = icsneoc2_netid_aux, + J1850VPW = icsneoc2_netid_j1850vpw, + ISO9141 = icsneoc2_netid_iso9141, + DiskData = icsneoc2_netid_disk_data, + Main51 = icsneoc2_netid_main51, + RED = icsneoc2_netid_red, + SCI = icsneoc2_netid_sci, + ISO9141_2 = icsneoc2_netid_iso9141_2, + ISO14230 = icsneoc2_netid_iso14230, + LIN = icsneoc2_netid_lin, + OP_Ethernet1 = icsneoc2_netid_op_ethernet1, + OP_Ethernet2 = icsneoc2_netid_op_ethernet2, + OP_Ethernet3 = icsneoc2_netid_op_ethernet3, // START Device Command Returns // When we send a command, the device returns on one of these, depending on command - RED_EXT_MEMORYREAD = 20, - RED_INT_MEMORYREAD = 21, - RED_DFLASH_READ = 22, - NeoMemorySDRead = 23, // Response from NeoMemory (MemoryTypeSD) - CAN_ERRBITS = 24, - NeoMemoryWriteDone = 25, - RED_WAVE_CAN1_LOGICAL = 26, - RED_WAVE_CAN2_LOGICAL = 27, - RED_WAVE_LIN1_LOGICAL = 28, - RED_WAVE_LIN2_LOGICAL = 29, - RED_WAVE_LIN1_ANALOG = 30, - RED_WAVE_LIN2_ANALOG = 31, - RED_WAVE_MISC_ANALOG = 32, - RED_WAVE_MISCDIO2_LOGICAL = 33, - RED_NETWORK_COM_ENABLE_EX = 34, - RED_NEOVI_NETWORK = 35, - RED_READ_BAUD_SETTINGS = 36, - RED_OLDFORMAT = 37, - RED_SCOPE_CAPTURE = 38, - RED_HARDWARE_EXCEP = 39, - RED_GET_RTC = 40, + RED_EXT_MEMORYREAD = icsneoc2_netid_red_ext_memoryread, + RED_INT_MEMORYREAD = icsneoc2_netid_red_int_memoryread, + RED_DFLASH_READ = icsneoc2_netid_red_dflash_read, + NeoMemorySDRead = icsneoc2_netid_neo_memory_sdread, // Response from NeoMemory (MemoryTypeSD) + CAN_ERRBITS = icsneoc2_netid_can_errbits, + NeoMemoryWriteDone = icsneoc2_netid_neo_memory_write_done, + RED_WAVE_CAN1_LOGICAL = icsneoc2_netid_red_wave_can1_logical, + RED_WAVE_CAN2_LOGICAL = icsneoc2_netid_red_wave_can2_logical, + RED_WAVE_LIN1_LOGICAL = icsneoc2_netid_red_wave_lin1_logical, + RED_WAVE_LIN2_LOGICAL = icsneoc2_netid_red_wave_lin2_logical, + RED_WAVE_LIN1_ANALOG = icsneoc2_netid_red_wave_lin1_analog, + RED_WAVE_LIN2_ANALOG = icsneoc2_netid_red_wave_lin2_analog, + RED_WAVE_MISC_ANALOG = icsneoc2_netid_red_wave_misc_analog, + RED_WAVE_MISCDIO2_LOGICAL = icsneoc2_netid_red_wave_miscdio2_logical, + RED_NETWORK_COM_ENABLE_EX = icsneoc2_netid_red_network_com_enable_ex, + RED_NEOVI_NETWORK = icsneoc2_netid_red_neovi_network, + RED_READ_BAUD_SETTINGS = icsneoc2_netid_red_read_baud_settings, + RED_OLDFORMAT = icsneoc2_netid_red_oldformat, + RED_SCOPE_CAPTURE = icsneoc2_netid_red_scope_capture, + RED_HARDWARE_EXCEP = icsneoc2_netid_red_hardware_excep, + RED_GET_RTC = icsneoc2_netid_red_get_rtc, // END Device Command Returns - ISO9141_3 = 41, - HSCAN2 = 42, - HSCAN3 = 44, - OP_Ethernet4 = 45, - OP_Ethernet5 = 46, - ISO9141_4 = 47, - LIN2 = 48, - LIN3 = 49, - LIN4 = 50, + ISO9141_3 = icsneoc2_netid_iso9141_3, + HSCAN2 = icsneoc2_netid_hscan2, + HSCAN3 = icsneoc2_netid_hscan3, + OP_Ethernet4 = icsneoc2_netid_op_ethernet4, + OP_Ethernet5 = icsneoc2_netid_op_ethernet5, + ISO9141_4 = icsneoc2_netid_iso9141_4, + LIN2 = icsneoc2_netid_lin2, + LIN3 = icsneoc2_netid_lin3, + LIN4 = icsneoc2_netid_lin4, // MOST = 51, Old and unused - RED_App_Error = 52, - CGI = 53, - Reset_Status = 54, - FB_Status = 55, - App_Signal_Status = 56, - Read_Datalink_Cm_Tx_Msg = 57, - Read_Datalink_Cm_Rx_Msg = 58, - Logging_Overflow = 59, - ReadSettings = 60, - HSCAN4 = 61, - HSCAN5 = 62, - RS232 = 63, - UART = 64, - UART2 = 65, - UART3 = 66, - UART4 = 67, - SWCAN2 = 68, - Ethernet_DAQ = 69, - Data_To_Host = 70, - TextAPI_To_Host = 71, - SPI1 = 72, - OP_Ethernet6 = 73, - Red_VBat = 74, - OP_Ethernet7 = 75, - OP_Ethernet8 = 76, - OP_Ethernet9 = 77, - OP_Ethernet10 = 78, - OP_Ethernet11 = 79, - FlexRay1a = 80, - FlexRay1b = 81, - FlexRay2a = 82, - FlexRay2b = 83, - LIN5 = 84, - FlexRay = 85, - FlexRay2 = 86, - OP_Ethernet12 = 87, - I2C = 88, - MOST25 = 90, - MOST50 = 91, - MOST150 = 92, - Ethernet = 93, - GMFSA = 94, - TCP = 95, - HSCAN6 = 96, - HSCAN7 = 97, - LIN6 = 98, - LSFTCAN2 = 99, - LogicalDiskInfo = 187, - WiVICommand = 221, - ScriptStatus = 224, - EthPHYControl = 239, - ExtendedCommand = 240, - ExtendedData = 242, - FlexRayControl = 243, - CoreMiniPreLoad = 244, - HW_COM_Latency_Test = 512, - DeviceStatus = 513, - UDP = 514, - ForwardedMessage = 516, - I2C2 = 517, - I2C3 = 518, - I2C4 = 519, - Ethernet2 = 520, - A2B1 = 522, - A2B2 = 523, - Ethernet3 = 524, - WBMS = 532, - DWCAN9 = 534, - DWCAN10 = 535, - DWCAN11 = 536, - DWCAN12 = 537, - DWCAN13 = 538, - DWCAN14 = 539, - DWCAN15 = 540, - DWCAN16 = 541, - LIN7 = 542, - LIN8 = 543, - SPI2 = 544, - MDIO1 = 545, - MDIO2 = 546, - MDIO3 = 547, - MDIO4 = 548, - MDIO5 = 549, - MDIO6 = 550, - MDIO7 = 551, - MDIO8 = 552, - OP_Ethernet13 = 553, - OP_Ethernet14 = 554, - OP_Ethernet15 = 555, - OP_Ethernet16 = 556, - SPI3 = 557, - SPI4 = 558, - SPI5 = 559, - SPI6 = 560, - SPI7 = 561, - SPI8 = 562, - LIN9 = 563, - LIN10 = 564, - LIN11 = 565, - LIN12 = 566, - LIN13 = 567, - LIN14 = 568, - LIN15 = 569, - LIN16 = 570, - Any = 0xfffe, // Never actually set as type, but used as flag for filtering - Invalid = 0xffff + RED_App_Error = icsneoc2_netid_red_app_error, + CGI = icsneoc2_netid_cgi, + Reset_Status = icsneoc2_netid_reset_status, + FB_Status = icsneoc2_netid_fb_status, + App_Signal_Status = icsneoc2_netid_app_signal_status, + Read_Datalink_Cm_Tx_Msg = icsneoc2_netid_read_datalink_cm_tx_msg, + Read_Datalink_Cm_Rx_Msg = icsneoc2_netid_read_datalink_cm_rx_msg, + Logging_Overflow = icsneoc2_netid_logging_overflow, + ReadSettings = icsneoc2_netid_read_settings, + HSCAN4 = icsneoc2_netid_hscan4, + HSCAN5 = icsneoc2_netid_hscan5, + RS232 = icsneoc2_netid_rs232, + UART = icsneoc2_netid_uart, + UART2 = icsneoc2_netid_uart2, + UART3 = icsneoc2_netid_uart3, + UART4 = icsneoc2_netid_uart4, + SWCAN2 = icsneoc2_netid_swcan2, + Ethernet_DAQ = icsneoc2_netid_ethernet_daq, + Data_To_Host = icsneoc2_netid_data_to_host, + TextAPI_To_Host = icsneoc2_netid_textapi_to_host, + SPI1 = icsneoc2_netid_spi1, + OP_Ethernet6 = icsneoc2_netid_op_ethernet6, + Red_VBat = icsneoc2_netid_red_vbat, + OP_Ethernet7 = icsneoc2_netid_op_ethernet7, + OP_Ethernet8 = icsneoc2_netid_op_ethernet8, + OP_Ethernet9 = icsneoc2_netid_op_ethernet9, + OP_Ethernet10 = icsneoc2_netid_op_ethernet10, + OP_Ethernet11 = icsneoc2_netid_op_ethernet11, + FlexRay1a = icsneoc2_netid_flexray1a, + FlexRay1b = icsneoc2_netid_flexray1b, + FlexRay2a = icsneoc2_netid_flexray2a, + FlexRay2b = icsneoc2_netid_flexray2b, + LIN5 = icsneoc2_netid_lin5, + FlexRay = icsneoc2_netid_flexray, + FlexRay2 = icsneoc2_netid_flexray2, + OP_Ethernet12 = icsneoc2_netid_op_ethernet12, + I2C = icsneoc2_netid_i2c, + MOST25 = icsneoc2_netid_most25, + MOST50 = icsneoc2_netid_most50, + MOST150 = icsneoc2_netid_most150, + Ethernet = icsneoc2_netid_ethernet, + GMFSA = icsneoc2_netid_gmfsa, + TCP = icsneoc2_netid_tcp, + HSCAN6 = icsneoc2_netid_hscan6, + HSCAN7 = icsneoc2_netid_hscan7, + LIN6 = icsneoc2_netid_lin6, + LSFTCAN2 = icsneoc2_netid_lsftcan2, + LogicalDiskInfo = icsneoc2_netid_logical_disk_info, + WiVICommand = icsneoc2_netid_wivi_command, + ScriptStatus = icsneoc2_netid_script_status, + EthPHYControl = icsneoc2_netid_eth_phy_control, + ExtendedCommand = icsneoc2_netid_extended_command, + ExtendedData = icsneoc2_netid_extended_data, + FlexRayControl = icsneoc2_netid_flexray_control, + CoreMiniPreLoad = icsneoc2_netid_coremini_preload, + HW_COM_Latency_Test = icsneoc2_netid_hw_com_latency_test, + DeviceStatus = icsneoc2_netid_device_status, + UDP = icsneoc2_netid_udp, + ForwardedMessage = icsneoc2_netid_forwarded_message, + I2C2 = icsneoc2_netid_i2c2, + I2C3 = icsneoc2_netid_i2c3, + I2C4 = icsneoc2_netid_i2c4, + Ethernet2 = icsneoc2_netid_ethernet2, + A2B1 = icsneoc2_netid_a2b1, + A2B2 = icsneoc2_netid_a2b2, + Ethernet3 = icsneoc2_netid_ethernet3, + WBMS = icsneoc2_netid_wbms, + DWCAN9 = icsneoc2_netid_dwcan9, + DWCAN10 = icsneoc2_netid_dwcan10, + DWCAN11 = icsneoc2_netid_dwcan11, + DWCAN12 = icsneoc2_netid_dwcan12, + DWCAN13 = icsneoc2_netid_dwcan13, + DWCAN14 = icsneoc2_netid_dwcan14, + DWCAN15 = icsneoc2_netid_dwcan15, + DWCAN16 = icsneoc2_netid_dwcan16, + LIN7 = icsneoc2_netid_lin7, + LIN8 = icsneoc2_netid_lin8, + SPI2 = icsneoc2_netid_spi2, + MDIO1 = icsneoc2_netid_mdio1, + MDIO2 = icsneoc2_netid_mdio2, + MDIO3 = icsneoc2_netid_mdio3, + MDIO4 = icsneoc2_netid_mdio4, + MDIO5 = icsneoc2_netid_mdio5, + MDIO6 = icsneoc2_netid_mdio6, + MDIO7 = icsneoc2_netid_mdio7, + MDIO8 = icsneoc2_netid_mdio8, + OP_Ethernet13 = icsneoc2_netid_op_ethernet13, + OP_Ethernet14 = icsneoc2_netid_op_ethernet14, + OP_Ethernet15 = icsneoc2_netid_op_ethernet15, + OP_Ethernet16 = icsneoc2_netid_op_ethernet16, + SPI3 = icsneoc2_netid_spi3, + SPI4 = icsneoc2_netid_spi4, + SPI5 = icsneoc2_netid_spi5, + SPI6 = icsneoc2_netid_spi6, + SPI7 = icsneoc2_netid_spi7, + SPI8 = icsneoc2_netid_spi8, + LIN9 = icsneoc2_netid_lin9, + LIN10 = icsneoc2_netid_lin10, + LIN11 = icsneoc2_netid_lin11, + LIN12 = icsneoc2_netid_lin12, + LIN13 = icsneoc2_netid_lin13, + LIN14 = icsneoc2_netid_lin14, + LIN15 = icsneoc2_netid_lin15, + LIN16 = icsneoc2_netid_lin16, + Any = icsneoc2_netid_any, // Never actually set as type, but used as flag for filtering + Invalid = icsneoc2_netid_invalid }; - enum class Type : neonettype_t { - Invalid = 0, - Internal = 1, // Used for statuses that don't actually need to be transferred to the client application - CAN = 2, - LIN = 3, - FlexRay = 4, - MOST = 5, - Ethernet = 6, - LSFTCAN = 7, - SWCAN = 8, - ISO9141 = 9, - I2C = 10, - A2B = 11, - SPI = 12, - MDIO = 13, - Any = 0xFE, // Never actually set as type, but used as flag for filtering - Other = 0xFF + enum class Type : icsneoc2_msg_bus_type_t { + Invalid = icsneoc2_msg_bus_type_invalid, + Internal = icsneoc2_msg_bus_type_internal, // Used for statuses that don't actually need to be transferred to the client application + CAN = icsneoc2_msg_bus_type_can, + LIN = icsneoc2_msg_bus_type_lin, + FlexRay = icsneoc2_msg_bus_type_flexray, + MOST = icsneoc2_msg_bus_type_most, + Ethernet = icsneoc2_msg_bus_type_ethernet, + LSFTCAN = icsneoc2_msg_bus_type_lsftcan, + SWCAN = icsneoc2_msg_bus_type_swcan, + ISO9141 = icsneoc2_msg_bus_type_iso9141, + I2C = icsneoc2_msg_bus_type_i2c, + A2B = icsneoc2_msg_bus_type_a2b, + SPI = icsneoc2_msg_bus_type_spi, + MDIO = icsneoc2_msg_bus_type_mdio, + Any = icsneoc2_msg_bus_type_any, // Never actually set as type, but used as flag for filtering + Other = icsneoc2_msg_bus_type_other }; enum class CoreMini : uint8_t { HSCAN = 0, diff --git a/include/icsneo/communication/packet/canpacket.h b/include/icsneo/communication/packet/canpacket.h index ede29cc..16e8581 100644 --- a/include/icsneo/communication/packet/canpacket.h +++ b/include/icsneo/communication/packet/canpacket.h @@ -13,6 +13,9 @@ namespace icsneo { typedef uint16_t icscm_bitfield; +std::optional CAN_DLCToLength(uint8_t length, bool fd); +std::optional CAN_LengthToDLC(size_t dataLength, bool fd); + #pragma pack(push,2) struct HardwareCANPacket { static std::shared_ptr DecodeToMessage(const std::vector& bytestream); @@ -78,4 +81,4 @@ struct HardwareCANErrorPacket { #endif // __cplusplus -#endif \ No newline at end of file +#endif diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index 8a67c5b..a58d99a 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -213,8 +213,8 @@ public: int addMessageCallback(const std::shared_ptr& cb) { return com->addMessageCallback(cb); } bool removeMessageCallback(int id) { return com->removeMessageCallback(id); } - bool transmit(std::shared_ptr frame); - bool transmit(std::vector> frames); + bool transmit(std::shared_ptr frame); + bool transmit(std::vector> frames); void setWriteBlocks(bool blocks); @@ -839,7 +839,7 @@ protected: void handleInternalMessage(std::shared_ptr message); - virtual void handleDeviceStatus(const std::shared_ptr&) {} + virtual void handleDeviceStatus(const std::shared_ptr&) {} neodevice_t& getWritableNeoDevice() { return data; } diff --git a/include/icsneo/device/devicetype.h b/include/icsneo/device/devicetype.h index 64b5ee8..8c14bd3 100644 --- a/include/icsneo/device/devicetype.h +++ b/include/icsneo/device/devicetype.h @@ -14,7 +14,9 @@ typedef uint32_t devicetype_t; #include #include -typedef uint32_t devicetype_t; +#include + +typedef icsneoc2_devicetype_t devicetype_t; namespace icsneo { @@ -22,65 +24,65 @@ class DeviceType { public: // This enum used to be a bitfield, but has since become an enum as we have more than 32 devices // Adding something? Make sure you update the type string and C-compatible defines below! - enum Enum : devicetype_t { - Unknown = (0x00000000), - BLUE = (0x00000001), - ECU_AVB = (0x00000002), - RADSupermoon = (0x00000003), - DW_VCAN = (0x00000004), - RADMoon2 = (0x00000005), - RADMars = (0x00000006), - VCAN4_1 = (0x00000007), - FIRE = (0x00000008), - RADPluto = (0x00000009), - VCAN4_2EL = (0x0000000a), - RADIO_CANHUB = (0x0000000b), - NEOECU12 = (0x0000000c), - OBD2_LCBADGE = (0x0000000d), - RADMoonDuo = (0x0000000e), - FIRE3 = (0x0000000f), - VCAN3 = (0x00000010), - RADJupiter = (0x00000011), - VCAN4_IND = (0x00000012), - RADGigastar = (0x00000013), - RED2 = (0x00000014), - EtherBADGE = (0x00000016), - RAD_A2B = (0x00000017), - RADEpsilon = (0x00000018), - RADGalaxy2 = (0x00000021), - RADMoon3 = (0x00000023), - RADComet = (0x00000024), - FIRE3_FlexRay = (0x00000025), - Connect = (0x00000026), - RADComet3 = (0x00000027), - RADMoonT1S = (0x00000028), - RADGigastar2 = (0x00000029), - RED = (0x00000040), - ECU = (0x00000080), - IEVB = (0x00000100), - Pendant = (0x00000200), - OBD2_PRO = (0x00000400), - ECUChip_UART = (0x00000800), - PLASMA = (0x00001000), - DONT_REUSE0 = (0x00002000), // Previously FIRE_VNET - NEOAnalog = (0x00004000), - CT_OBD = (0x00008000), - DONT_REUSE1 = (0x00010000), // Previously PLASMA_1_12 - DONT_REUSE2 = (0x00020000), // Previously PLASMA_1_13 - ION = (0x00040000), - RADStar = (0x00080000), - DONT_REUSE3 = (0x00100000), // Previously ION3 - VCAN4_4 = (0x00200000), - VCAN4_2 = (0x00400000), - CMProbe = (0x00800000), - EEVB = (0x01000000), - VCANrf = (0x02000000), - FIRE2 = (0x04000000), - Flex = (0x08000000), - RADGalaxy = (0x10000000), - RADStar2 = (0x20000000), - VividCAN = (0x40000000), - OBD2_SIM = (0x80000000) + enum Enum : icsneoc2_devicetype_t { + Unknown = icsneoc2_devicetype_unknown, + BLUE = icsneoc2_devicetype_blue, + ECU_AVB = icsneoc2_devicetype_ecu_avb, + RADSupermoon = icsneoc2_devicetype_rad_supermoon, + DW_VCAN = icsneoc2_devicetype_dw_vcan, + RADMoon2 = icsneoc2_devicetype_rad_moon2, + RADMars = icsneoc2_devicetype_rad_mars, + VCAN4_1 = icsneoc2_devicetype_vcan41, + FIRE = icsneoc2_devicetype_fire, + RADPluto = icsneoc2_devicetype_rad_pluto, + VCAN4_2EL = icsneoc2_devicetype_vcan42_el, + RADIO_CANHUB = icsneoc2_devicetype_radio_canhub, + NEOECU12 = icsneoc2_devicetype_neo_ecu12, + OBD2_LCBADGE = icsneoc2_devicetype_obd2_lc_badge, + RADMoonDuo = icsneoc2_devicetype_rad_moon_duo, + FIRE3 = icsneoc2_devicetype_fire3, + VCAN3 = icsneoc2_devicetype_vcan3, + RADJupiter = icsneoc2_devicetype_rad_jupiter, + VCAN4_IND = icsneoc2_devicetype_vcan4_industrial, + RADGigastar = icsneoc2_devicetype_rad_gigastar, + RED2 = icsneoc2_devicetype_red2, + EtherBADGE = icsneoc2_devicetype_etherbadge, + RAD_A2B = icsneoc2_devicetype_rad_a2b, + RADEpsilon = icsneoc2_devicetype_rad_epsilon, + RADGalaxy2 = icsneoc2_devicetype_rad_galaxy2, + RADMoon3 = icsneoc2_devicetype_rad_moon3, + RADComet = icsneoc2_devicetype_rad_comet, + FIRE3_FlexRay = icsneoc2_devicetype_fire3_flexray, + Connect = icsneoc2_devicetype_connect, + RADComet3 = icsneoc2_devicetype_rad_comet3, + RADMoonT1S = icsneoc2_devicetype_rad_moon_t1s, + RADGigastar2 = icsneoc2_devicetype_rad_gigastar2, + RED = icsneoc2_devicetype_red, + ECU = icsneoc2_devicetype_ecu, + IEVB = icsneoc2_devicetype_ievb, + Pendant = icsneoc2_devicetype_pendant, + OBD2_PRO = icsneoc2_devicetype_obd2_pro, + ECUChip_UART = icsneoc2_devicetype_ecuchip_uart, + PLASMA = icsneoc2_devicetype_plasma, + //DONT_REUSE0 = , // Previously FIRE_VNET + NEOAnalog = icsneoc2_devicetype_neo_analog, + CT_OBD = icsneoc2_devicetype_ct_obd, + //DONT_REUSE1 = (0x00010000), // Previously PLASMA_1_12 + //DONT_REUSE2 = (0x00020000), // Previously PLASMA_1_13 + ION = icsneoc2_devicetype_ion, + RADStar = icsneoc2_devicetype_rad_star, + //DONT_REUSE3 = (0x00100000), // Previously ION3 + VCAN4_4 = icsneoc2_devicetype_vcan44, + VCAN4_2 = icsneoc2_devicetype_vcan42, + CMProbe = icsneoc2_devicetype_cm_probe, + EEVB = icsneoc2_devicetype_eevb, + VCANrf = icsneoc2_devicetype_vcan_rf, + FIRE2 = icsneoc2_devicetype_fire2, + Flex = icsneoc2_devicetype_flex, + RADGalaxy = icsneoc2_devicetype_rad_galaxy, + RADStar2 = icsneoc2_devicetype_rad_star2, + VividCAN = icsneoc2_devicetype_vividcan, + OBD2_SIM = icsneoc2_devicetype_obd2_sim }; /** @@ -90,9 +92,10 @@ public: * as the product name may change based on device-specific factors, such as serial * number. */ - static const char* GetGenericProductName(DeviceType::Enum type) { + template + static std::string GetGenericProductName(T deviceType) { // Adding something? Make sure you update DEVICE_TYPE_LONGEST_NAME at the top! - switch(type) { + switch(static_cast(deviceType)) { case Unknown: return "Unknown"; case BLUE: @@ -201,12 +204,7 @@ public: return "neoVI Connect"; case RADGigastar2: return "RAD-Gigastar 2"; - case DONT_REUSE0: - case DONT_REUSE1: - case DONT_REUSE2: - case DONT_REUSE3: - // Intentionally don't use default so that the compiler throws a warning when something is added - return "Unknown neoVI"; + // Intentionally don't use default so that the compiler throws a warning when something is added } return "Unknown neoVI"; } @@ -214,8 +212,8 @@ public: DeviceType() { value = DeviceType::Enum::Unknown; } DeviceType(devicetype_t netid) { value = (DeviceType::Enum)netid; } DeviceType(DeviceType::Enum netid) { value = netid; } - DeviceType::Enum getDeviceType() const { return value; } - std::string getGenericProductName() const { return GetGenericProductName(getDeviceType()); } + icsneoc2_devicetype_t getDeviceType() const { return value; } + std::string getGenericProductName() const { return DeviceType::GetGenericProductName(getDeviceType()); } operator devicetype_t() const { return getDeviceType(); } private: diff --git a/include/icsneo/device/extensions/deviceextension.h b/include/icsneo/device/extensions/deviceextension.h index b45596f..445999c 100644 --- a/include/icsneo/device/extensions/deviceextension.h +++ b/include/icsneo/device/extensions/deviceextension.h @@ -33,7 +33,7 @@ public: virtual void handleMessage(const std::shared_ptr&) {} // Return true to continue transmitting, success should be written to if false is returned - virtual bool transmitHook(const std::shared_ptr& frame, bool& success) { (void)frame; (void)success; return true; } + virtual bool transmitHook(const std::shared_ptr& frame, bool& success) { (void)frame; (void)success; return true; } protected: Device& device; diff --git a/include/icsneo/device/extensions/flexray/extension.h b/include/icsneo/device/extensions/flexray/extension.h index 0d70268..52fb487 100644 --- a/include/icsneo/device/extensions/flexray/extension.h +++ b/include/icsneo/device/extensions/flexray/extension.h @@ -25,7 +25,7 @@ public: void onGoOffline() override; void handleMessage(const std::shared_ptr& message) override; - bool transmitHook(const std::shared_ptr& frame, bool& success) override; + bool transmitHook(const std::shared_ptr& frame, bool& success) override; std::shared_ptr getController(uint8_t index) const { if(index >= controllers.size()) diff --git a/include/icsneo/device/tree/neovifire2/neovifire2.h b/include/icsneo/device/tree/neovifire2/neovifire2.h index eaaac6e..c74555e 100644 --- a/include/icsneo/device/tree/neovifire2/neovifire2.h +++ b/include/icsneo/device/tree/neovifire2/neovifire2.h @@ -121,7 +121,7 @@ protected: // The supported TX networks are the same as the supported RX networks for this device void setupSupportedTXNetworks(std::vector& txNetworks) override { setupSupportedRXNetworks(txNetworks); } - void handleDeviceStatus(const std::shared_ptr& message) override { + void handleDeviceStatus(const std::shared_ptr& message) override { if(message->data.size() < sizeof(neovifire2_status_t)) return; std::lock_guard lk(ioMutex); diff --git a/include/icsneo/device/tree/plasion/plasion.h b/include/icsneo/device/tree/plasion/plasion.h index b629d9b..4dbcf8f 100644 --- a/include/icsneo/device/tree/plasion/plasion.h +++ b/include/icsneo/device/tree/plasion/plasion.h @@ -81,7 +81,7 @@ protected: return ret; } - void handleDeviceStatus(const std::shared_ptr& message) override { + void handleDeviceStatus(const std::shared_ptr& message) override { if(message->data.size() < sizeof(fire2vnet_status_t)) return; std::lock_guard lk(ioMutex); diff --git a/include/icsneo/device/tree/radgalaxy/radgalaxy.h b/include/icsneo/device/tree/radgalaxy/radgalaxy.h index e9e3049..3491949 100644 --- a/include/icsneo/device/tree/radgalaxy/radgalaxy.h +++ b/include/icsneo/device/tree/radgalaxy/radgalaxy.h @@ -92,7 +92,7 @@ protected: // The supported TX networks are the same as the supported RX networks for this device void setupSupportedTXNetworks(std::vector& txNetworks) override { setupSupportedRXNetworks(txNetworks); } - void handleDeviceStatus(const std::shared_ptr& message) override { + void handleDeviceStatus(const std::shared_ptr& message) override { if(message->data.size() < sizeof(radgalaxy_status_t)) return; std::lock_guard lk(ioMutex); diff --git a/include/icsneo/device/tree/radgalaxy2/radgalaxy2.h b/include/icsneo/device/tree/radgalaxy2/radgalaxy2.h index 7eccfe9..7bd2967 100644 --- a/include/icsneo/device/tree/radgalaxy2/radgalaxy2.h +++ b/include/icsneo/device/tree/radgalaxy2/radgalaxy2.h @@ -97,7 +97,7 @@ protected: // The supported TX networks are the same as the supported RX networks for this device void setupSupportedTXNetworks(std::vector& txNetworks) override { setupSupportedRXNetworks(txNetworks); } - void handleDeviceStatus(const std::shared_ptr& message) override { + void handleDeviceStatus(const std::shared_ptr& message) override { if(message->data.size() < sizeof(radgalaxy2_status_t)) return; std::lock_guard lk(ioMutex); diff --git a/include/icsneo/device/tree/radgigastar/radgigastar.h b/include/icsneo/device/tree/radgigastar/radgigastar.h index 66b5a48..3012aee 100644 --- a/include/icsneo/device/tree/radgigastar/radgigastar.h +++ b/include/icsneo/device/tree/radgigastar/radgigastar.h @@ -105,7 +105,7 @@ protected: txNetworks.insert(txNetworks.end(), supportedTxNetworks.begin(), supportedTxNetworks.end()); } - void handleDeviceStatus(const std::shared_ptr& message) override { + void handleDeviceStatus(const std::shared_ptr& message) override { if(message->data.size() < sizeof(radgigastar_status_t)) return; std::lock_guard lk(ioMutex); diff --git a/include/icsneo/device/tree/radgigastar2/radgigastar2.h b/include/icsneo/device/tree/radgigastar2/radgigastar2.h index b82f940..a9af910 100644 --- a/include/icsneo/device/tree/radgigastar2/radgigastar2.h +++ b/include/icsneo/device/tree/radgigastar2/radgigastar2.h @@ -113,7 +113,7 @@ namespace icsneo // The supported TX networks are the same as the supported RX networks for this device void setupSupportedTXNetworks(std::vector &txNetworks) override { setupSupportedRXNetworks(txNetworks); } - void handleDeviceStatus(const std::shared_ptr &message) override + void handleDeviceStatus(const std::shared_ptr &message) override { if (message->data.size() < sizeof(radgigastar2_status_t)) return; diff --git a/include/icsneo/device/tree/radmars/radmars.h b/include/icsneo/device/tree/radmars/radmars.h index 4e72bc4..faf7d9b 100644 --- a/include/icsneo/device/tree/radmars/radmars.h +++ b/include/icsneo/device/tree/radmars/radmars.h @@ -97,7 +97,7 @@ protected: txNetworks.insert(txNetworks.end(), supportedTxNetworks.begin(), supportedTxNetworks.end()); } - void handleDeviceStatus(const std::shared_ptr& message) override { + void handleDeviceStatus(const std::shared_ptr& message) override { if(message->data.size() < sizeof(radmars_status_t)) return; std::lock_guard lk(ioMutex); diff --git a/include/icsneo/device/tree/valuecan4/valuecan4-2el.h b/include/icsneo/device/tree/valuecan4/valuecan4-2el.h index 7797e2b..1b735de 100644 --- a/include/icsneo/device/tree/valuecan4/valuecan4-2el.h +++ b/include/icsneo/device/tree/valuecan4/valuecan4-2el.h @@ -75,7 +75,7 @@ protected: size_t getEthernetActivationLineCount() const override { return 1; } - void handleDeviceStatus(const std::shared_ptr& message) override { + void handleDeviceStatus(const std::shared_ptr& message) override { if(message->data.size() < sizeof(valuecan4_2el_status_t)) return; std::lock_guard lk(ioMutex); diff --git a/include/icsneo/icsneoc2.h b/include/icsneo/icsneoc2.h new file mode 100644 index 0000000..30671b5 --- /dev/null +++ b/include/icsneo/icsneoc2.h @@ -0,0 +1,740 @@ +#pragma once + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_WIN32) || defined(_WIN64) + // Microsoft + #define EXPORT __declspec(dllexport) + #define IMPORT __declspec(dllimport) +#elif defined(__GNUC__) + // GCC + #define EXPORT __attribute__((visibility("default"))) + #define IMPORT +#else + // do nothing and hope for the best? + #define EXPORT + #define IMPORT + #pragma warning Unknown dynamic link import/export semantics. +#endif + +#ifdef ICSNEOC2_BUILD_STATIC +#define ICSNEOC2_API +#else +#ifdef ICSNEOC2_BUILD_DYNAMIC +#define ICSNEOC2_API EXPORT +#else +#define ICSNEOC2_API IMPORT +#endif // ICSNEOC2_BUILD_DYNAMIC +#endif // ICSNEOC2_BUILD_STATIC + +/** @brief icsneoc2_device_t opaque struct definition + * + * This object represents a single device found on the system. + * + * @see icsneo_find_devices + */ +typedef struct icsneoc2_device_t icsneoc2_device_t; + + +/** @brief icsneoc2_message_t opaque struct definition + * + * This object represents a single event from the device. + * + * @see icsneoc2_device_events_get + */ +typedef struct icsneoc2_message_t icsneoc2_message_t; + + +/** @brief icsneoc2_event_t opaque struct definition + * + * This object represents a single event from the device. + * + * @see icsneoc2_device_events_get + */ +typedef struct icsneoc2_event_t icsneoc2_event_t; + + +/** @brief Error codes for icsneo functions. + * + * This enum is guaranteed to be ABI stable, any new values will be appended to the end. + */ +typedef enum _icsneoc2_error_t { + // Function was successful + icsneoc2_error_success, + // Invalid parameters, typically because of a NULL reference. + icsneoc2_error_invalid_parameters, + // Error opening the device. + icsneoc2_error_open_failed, + // Error going online. + icsneoc2_error_go_online_failed, + // Error enabling message polling. + icsneoc2_error_enable_message_polling_failed, + // Error syncing RTC. + icsneoc2_error_sync_rtc_failed, + // Error getting messages. + icsneoc2_error_get_messages_failed, + // Generic invalid type error + icsneoc2_error_invalid_type, + // Generic RTC error code + icsneoc2_error_rtc_failure, + // Error setting settings + icsneoc2_error_set_settings_failure, + // Failed to transmit messages + icsneoc2_error_transmit_messages_failed, + // Failed to copy string to buffer + icsneoc2_error_string_copy_failed, + // Invalid device parameter + icsneoc2_error_invalid_device, + // Invalid message parameter + icsneoc2_error_invalid_message, + // NOTE: Any new values added here should be updated in icsneoc2_error_code_get +} _icsneoc2_error_t; + +/** @brief Integer representation of _icsneoc2_error_t enum. + * + * This is used for easier ABI compatibility, especially between other languages. + */ +typedef uint32_t icsneoc2_error_t; + + +/** @brief Get the error string for an error code. + * + * @param[in] icsneoc2_error_t error_code The error code to get the description of. + * @param[out] const char* value Pointer to a buffer to copy the description into. Null terminated. + * @param[in,out] uint32_t* value_length Size of the value buffer. Modified with the length of the description. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_error_code_get(icsneoc2_error_t error_code, const char* value, uint32_t* value_length); + +/** @brief Get the device type string for a icsneoc2_devicetype_t. + * + * @param[in] icsneoc2_devicetype_t device_type The device type to get the description of. + * @param[out] const char* value Pointer to a buffer to copy the description into. Null terminated. + * @param[in,out] uint32_t* value_length Size of the value buffer. Modified with the length of the description. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_type_name_get(icsneoc2_devicetype_t device_type, const char* value, uint32_t* value_length); + +/** @brief Find all hardware attached to the system. + * + * @param[out] icsneoc2_device_t array of devices to be filled with found devices. + * Undefined behaviour if index is out of range of devices_count. + * @param[in,out] uint32_t* devices_count Size of the devices array. Modified with the number of devices found. + * @param[in] void* reserved Reserved for future use. Currently unused and must be set to NULL. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_find_all(icsneoc2_device_t** devices, uint32_t* devices_count, void* reserved); + +/** @brief Check to make sure a device is valid. + * + * @param[in] icsneoc2_device_t device The device to check. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_is_valid(icsneoc2_device_t* device); + +/** @brief Check to make sure a device is open. + * + * @param[in] icsneoc2_device_t device The device to check. + * @param[out] bool is_open true if the device is open, false otherwise + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_is_open(icsneoc2_device_t* device, bool* is_open); + +/** @brief Check see if a device is disconnected. + * + * @param[in] icsneoc2_device_t device The device to check. + * @param[out] bool is_disconnected true if the device is open, false otherwise + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_is_disconnected(icsneoc2_device_t* device, bool* is_disconnected); + +/** @brief Get the open options for a device + * + * @param[in] icsneoc2_device_t device The device to set options for. + * @param[in] icsneoc2_open_options_t options Options to set for the device. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_open_options_get(icsneoc2_device_t* device, icsneoc2_open_options_t* options); + +/** @brief Set the open options for a device + * + * @param[in] icsneoc2_device_t device The device to set options for. + * @param[in] icsneoc2_open_options_t options Options to set for the device. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_open_options_set(icsneoc2_device_t* device, icsneoc2_open_options_t options); + +/** @brief Open a connection to a device. + * + * After a successful call to this function, icsneoc2_device_close() must be called to close the device. + * + * @param[in] icsneoc2_device_t device The device to open. + * @param[out] icsneo_handle_t* handle Pointer to a handle to the opened device. Will be NULL on failure. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_failure otherwise. + * + * @see icsneoc2_device_close + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_open(icsneoc2_device_t* device); + +/** @brief 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. + * + * @param[in] icsneoc2_device_t device The device to close. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_failure otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_close(icsneoc2_device_t* device); + +/** @brief Get the description of a device + * + * @param[in] icsneoc2_device_t device The device to get the description of. + * @param[out] const char* value Pointer to a buffer to copy the description into. Null terminated. + * @param[in,out] uint32_t* value_length Size of the value buffer. Modified with the length of the description. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_description_get(icsneoc2_device_t* device, const char* value, uint32_t* value_length); + +/** @brief Get the type of a device + * + * @param[in] icsneoc2_device_t device The device to get the type of. + * @param[out] icsneoc2_devicetype_t* value Pointer to an icsneoc2_devicetype_t to copy the type into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_type_get(icsneoc2_device_t* device, icsneoc2_devicetype_t* value); + +/** @brief Get the serial of a device + * + * @param[in] icsneoc2_device_t device The device to get the serial of. + * @param[out] const char* value Pointer to a buffer to copy the serial into. Null terminated. + * @param[in,out] uint32_t* value_length Size of the value buffer. Modified with the length of the serial. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_serial_get(icsneoc2_device_t* device, const char* value, uint32_t* value_length); + +/** @brief Set the online state of a device. + * + * @param[in] icsneoc2_device_t device The device to set the online state of. + * @param[in] bool go_online true to go online, false to go offline. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_go_online(icsneoc2_device_t* device, bool go_online); + +/** @brief Get the online state of a device. + * + * @param[in] icsneoc2_device_t device The device to get the online state of. + * @param[out] bool true if online, false if offline. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_is_online(icsneoc2_device_t* device, bool* is_online); + +/** @brief Get the online supported state of a device. + * + * @param[in] icsneoc2_device_t device The device to get the online supported state of. + * @param[out] bool true if online, false if offline. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_is_online_supported(icsneoc2_device_t* device, bool* is_online_supported); + + +/** @brief Set the message polling state of a device. + * + * @param[in] icsneoc2_device_t device The device to set the message polling state of. + * @param[in] bool enable true to enable message polling, false to disable message polling.. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_message_polling_set(icsneoc2_device_t* device, bool enable); + +/** @brief Get the message polling state of a device. + * + * @param[in] icsneoc2_device_t device The device to set the message polling state of. + * @param[out] bool is_enabled true if message polling is enabled, false if message polling is disabled. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_message_polling_get(icsneoc2_device_t* device, bool* is_enabled); + +/** @brief Set the message polling limit of a device. + * + * This will truncate the message queue to the specified limit. + * + * @param[in] icsneoc2_device_t device The device to enforce the message polling limit. + * @param[in] uint32_t limit The limit to enforce. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_message_polling_set_limit(icsneoc2_device_t* device, uint32_t limit); + +/** @brief Get the message polling limit of a device. + * + * @see icsneoc2_device_message_polling_set_limit + * + * @param[in] icsneoc2_device_t device The device to enforce the message polling limit. + * @param[out] uint32_t limit The limit to get. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_message_polling_limit_get(icsneoc2_device_t* device, uint32_t* limit); + +/** @brief Get the message count of a device + * + * @param[in] icsneoc2_device_t device The device to get the message count of. + * @param[out] uint32_t* count Pointer to a uint32_t to copy the message count into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_message_count_get(icsneoc2_device_t* device, uint32_t* count); + + +/** @brief Get the timestamp resolution (nanoseconds) of a device + * + * @param[in] icsneoc2_device_t device The device to get the timestamp resolution of. + * @param[out] uint32_t* resolution Pointer to a uint32_t to copy the timestamp resolution in nanoseconds into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_timestamp_resolution_get(icsneoc2_device_t* device, uint32_t* resolution); + +/** @brief Get the messages of a device + * + * When calling this function, the previous messages retrieved by this function will be invalid. + * + * @param[in] icsneoc2_device_t device The device to get the messages of. + * @param[out] icsneoc2_message_t** messages Pointer to an array of icsneoc2_message_t to copy the messages into. + * Undefined behaviour if index is out of range of messages_count. + * @param[in,out] uint32_t* messages_count Size of the messages array. Modified with the number of messages found. + * @param[in] uint32_t timeout_ms The timeout in milliseconds to wait for messages. A value of 0 indicates a non-blocking call. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_messages_get(icsneoc2_device_t* device, icsneoc2_message_t** messages, uint32_t* messages_count, uint32_t timeout_ms); + +/** @brief Transmit messages from a device + * + * @param[in] icsneoc2_device_t device The device to get the messages of. + * @param[out] icsneoc2_message_t** messages Pointer to an array of icsneoc2_message_t to copy the messages into. + * Undefined behaviour if index is out of range of messages_count. + * @param[in,out] uint32_t* messages_count Size of the messages array. Modified with the number of messages actually transmitted. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_transmission_failed otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_messages_transmit(icsneoc2_device_t* device, icsneoc2_message_t** messages, uint32_t* messages_count); + +/** @brief Check if a message is valid + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] bool* is_valid Pointer to a bool to copy the validity of the message into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_is_valid(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* is_valid); + +/** @brief Get the type of a message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] icsneoc2_msg_type_t* msg_type Pointer to a icsneoc2_msg_type_t to copy the type of the value into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + * + * @see icsneoc2_msg_type_t + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_type_get(icsneoc2_device_t* device, icsneoc2_message_t* message, icsneoc2_msg_type_t* msg_type); + +/** @brief Get the message type string for a icsneoc2_msg_bus_type_t. + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] const char* value Pointer to a buffer to copy the description into. Null terminated. + * @param[in,out] uint32_t* value_length Size of the value buffer. Modified with the length of the description. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_type_name_get(icsneoc2_msg_type_t msg_type, const char* value, uint32_t* value_length); + +/** @brief Get the type of a bus message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] icsneoc2_msg_type_t* msg_type Pointer to a icsneoc2_msg_type_t to copy the type of the value into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_invalid_type otherwise. + * + * @see icsneoc2_msg_bus_type_t, icsneoc2_bus_type_name_get + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_bus_type_get(icsneoc2_device_t* device, icsneoc2_message_t* message, icsneoc2_msg_bus_type_t* bus_type); + +/** @brief Get the bus type string for a icsneoc2_msg_bus_type_t. + * + * @param[in] icsneoc2_msg_bus_type_t bus_type The bus type to get the description of. + * @param[out] const char* value Pointer to a buffer to copy the description into. Null terminated. + * @param[in,out] uint32_t* value_length Size of the value buffer. Modified with the length of the description. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_bus_type_name_get(icsneoc2_msg_bus_type_t bus_type, const char* value, uint32_t* value_length); + +/** @brief Get the transmission status of a message. + * + * When a message is transmitted from the device, It will be returned in the receive buffer. + * @see icsneoc2_device_messages_transmit + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to modify. + * @param[out] bool value Pointer to a bool to copy the tranmission status into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_is_transmit(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value); + +/** @brief Get the Network ID (netid) of a bus message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] icsneoc2_netid_t* netid Pointer to a icsneoc2_netid_t to copy the type of the value into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters or icsneoc2_error_invalid_type otherwise. + * + * @see icsneoc2_netid_t, icsneoc2_netid_name_get + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_netid_get(icsneoc2_device_t* device, icsneoc2_message_t* message, icsneoc2_netid_t* netid); + +/** @brief Get the netid string for a icsneoc2_netid_t. + * + * @param[in] icsneoc2_netid_t netid The network id to get the description of. + * @param[out] const char* value Pointer to a buffer to copy the description into. Null terminated. + * @param[in,out] uint32_t* value_length Size of the value buffer. Modified with the length of the description. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_netid_name_get(icsneoc2_netid_t netid, const char* value, uint32_t* value_length); + +/** @brief Set the Network ID (netid) of a bus message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[in] icsneoc2_netid_t netid The netid to set. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_netid_set(icsneoc2_device_t* device, icsneoc2_message_t* message, icsneoc2_netid_t netid); + +/** @brief Set the data bytes of a message + * + * @note This function will not set the DLC of the message. @see icsneoc2_message_set_dlc + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to copy the data into. + * @param[out] uint8_t* data Pointer to a uint8_t array to copy from. + * @param[in] uint32_t data_length length of the data. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_data_set(icsneoc2_device_t* device, icsneoc2_message_t* message, uint8_t* data, uint32_t data_length); + +/** @brief Get the data bytes of a message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] uint8_t* data Pointer to a uint8_t to copy the data bytes into. + * @param[in,out] uint32_t* data_length Pointer to a uint32_t to copy the length of the data into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_data_get(icsneoc2_device_t* device, icsneoc2_message_t* message, uint8_t* data, uint32_t* data_length); + +/** @brief Get the Arbitration ID of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] uint32_t* value Pointer to a uint32_t to copy the Arbitration ID into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_arbid_get(icsneoc2_device_t* device, icsneoc2_message_t* message, uint32_t* value); + +/** @brief Set the Arbitration ID of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] uint32_t value Arbitration ID to set. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_arbid_set(icsneoc2_device_t* device, icsneoc2_message_t* message, uint32_t value); + +/** @brief Get the DLC on wire of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] uint32_t* value Pointer to a uint32_t to copy the DLC on wire into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_dlc_get(icsneoc2_device_t* device, icsneoc2_message_t* message, int32_t* value); + +/** @brief Set the DLC on wire of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] int32_t value DLC to set. Set to a negative value to auto calculate. Auto setting assumes data and + * canfd parameters are correct. Set to 0 on failure. @see icsneoc2_message_can_set_data and icsneoc2_message_can_canfd_set + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_dlc_set(icsneoc2_device_t* device, icsneoc2_message_t* message, int32_t value); + +/** @brief Get the Remote Transmission Request (RTR) status of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[in] int32_t value DLC to get. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_is_remote(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value); + +/** @brief Set the Remote Transmission Request (RTR) of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to modify. + * @param[out] bool value Remote status to set. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_set_remote(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value); + +/** @brief Get the extended status of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] bool* value Pointer to a uint32_t to copy the extended status into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_is_extended(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value); + +/** @brief Set the extended status of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to modify. + * @param[out] bool value Extended status to set. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_extended_set(icsneoc2_device_t* device, icsneoc2_message_t* message, bool value); + +/** @brief Get the CANFD status of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] bool* value Pointer to a uint32_t to copy the CANFD status into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_is_canfd(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value); + +/** @brief Set the CANFD status of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to modify. + * @param[out] bool value CANFD status to set. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_canfd_set(icsneoc2_device_t* device, icsneoc2_message_t* message, bool value); + +/** @brief Get the baudrate switch status (BRS) of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] bool* value Pointer to a uint32_t to copy the baudrate switch (BRS) status into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_baudrate_switch_get(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value); + +/** @brief Set the baudrate switch status (BRS) of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to modify. + * @param[out] bool value baudrate switch status (BRS) to set. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_baudrate_switch_set(icsneoc2_device_t* device, icsneoc2_message_t* message, bool value); + +/** @brief Get the error state indicator status of a CAN message + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[in] icsneoc2_message_t* message The message to check. + * @param[out] bool* value Pointer to a uint32_t to copy the error state indicator status into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_error_state_indicator_get(icsneoc2_device_t* device, icsneoc2_message_t* message, bool* value); + +/** @brief Create CAN messages for a device + * + * @param[in] icsneoc2_device_t device The device to get the messages of. + * @param[out] icsneoc2_message_t** messages Pointer to an array of icsneoc2_message_t to copy the messages into. + * Undefined behaviour if index is out of range of messages_count. + * @param[in] uint32_t* messages_count Size of the messages array. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_create(icsneoc2_device_t* device, icsneoc2_message_t** messages, uint32_t messages_count); + +/** @brief Free CAN messages for a device + * + * @param[in] icsneoc2_device_t device The device to free the messages of. + * @param[in] icsneoc2_message_t* message The message to free. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + * + * @warning This function should only be called on messages created by icsneoc2_message_can_create. + * + * @see icsneoc2_message_can_create + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_message_can_free(icsneoc2_device_t* device, icsneoc2_message_t* message); + +/** @brief Get the global events not specifically related to a device. + * + * @param[out] icsneoc2_event_t** events Pointer to an array of icsneoc2_event_t to copy the events into. + * Undefined behaviour if index is out of range of events_count. + * @param[in,out] uint32_t* events_count Size of the events array. Modified with the number of events found. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_events_get(icsneoc2_event_t** events, uint32_t* events_count); + +/** @brief Get the events of a device. + * + * @param[in] icsneoc2_device_t device The device to get the events of. + * @param[out] icsneoc2_event_t** events Pointer to an array of icsneoc2_event_t to copy the events into. + * Undefined behaviour if index is out of range of events_count. + * @param[in,out] uint32_t* events_count Size of the events array. Modified with the number of events found. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_events_get(icsneoc2_device_t* device, icsneoc2_event_t** events, uint32_t* events_count); + +/** @brief Get the error string for an error code. + * + * @param[in] icsneoc2_event_t* event The event to get the description of. + * @param[out] const char* value Pointer to a buffer to copy the description into. Null terminated. + * @param[in,out] uint32_t* value_length Size of the value buffer. Modified with the length of the description. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_event_description_get(icsneoc2_event_t* event, const char* value, uint32_t* value_length); + +/** @brief Get the RTC (Real time clock) of a device. + * + * @param[in] icsneoc2_device_t device The device to get the RTC of. + * @param[out] int64_t* unix_epoch Pointer to an int64_t to copy the RTC into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_rtc_get(icsneoc2_device_t* device, int64_t* unix_epoch); + +/** @brief Set the RTC (Real time clock) of a device. + * + * @param[in] icsneoc2_device_t device The device to get the RTC of. + * @param[in] int64_t unix_epoch int64_t to copy the RTC into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_rtc_set(icsneoc2_device_t* device, int64_t unix_epoch); + +/** @brief Load the default settings for a device + * + * @param[in] icsneoc2_device_t device The device to load the settings for. + * @param[in] bool save True to make the settings permanent, false will be reverted on power cycle. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. + */ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_load_default_settings(icsneoc2_device_t* device, bool save); + +/** @brief Get the baudrate for a network + * + * @note @see icsneoc2_device_canfd_baudrate_get for CANFD. + * + * @param[in] icsneoc2_device_t* device The device to get the baudrate value. + * @param[in] icsneoc2_netid_t netid The network to get the baudrate value. + * @param[in] uint64_t* baudrate The baudrate to get the network value. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_baudrate_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint64_t* baudrate); + +/** @brief Set the baudrate for a network + * + * @note @see icsneoc2_device_canfd_baudrate_set for CANFD. + * + * @param[in] icsneoc2_device_t* device The device to set the baudrate for. + * @param[in] icsneoc2_netid_t netid The network to set the baudrate for. + * @param[in] uint64_t baudrate The baudrate to set the network to. + * @param[in] bool save True to make the settings permanent, false will be reverted on power cycle. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_baudrate_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint64_t baudrate, bool save); + +/** @brief Get the baudrate for a CAN FD network + * + * @param[in] icsneoc2_device_t* device The device to get the baudrate value. + * @param[in] icsneoc2_netid_t netid The network to get the baudrate value. + * @param[in] uint64_t* baudrate The baudrate to get the network value. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_canfd_baudrate_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint64_t* baudrate); + +/** @brief Set the baudrate for a CANFD network + * + * @param[in] icsneoc2_device_t* device The device to set the baudrate for. + * @param[in] icsneoc2_netid_t netid The network to set the baudrate for. + * @param[in] uint64_t baudrate The baudrate to set the network to. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_canfd_baudrate_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint64_t baudrate, bool save); + +/** @brief Check if the device supports TC10. + * + * @param[in] icsneoc2_device_t* device The device to check against. + * @param[out] bool* supported Pointer to a uint32_t to copy the value into. + * + * @return icsneoc2_error_t icsneoc2_error_success if successful, icsneoc2_error_invalid_parameters otherwise. +*/ +ICSNEOC2_API icsneoc2_error_t icsneoc2_device_supports_tc10(icsneoc2_device_t* device, bool* supported); + +#ifdef __cplusplus +} +#endif diff --git a/include/icsneo/icsneoc2types.h b/include/icsneo/icsneoc2types.h new file mode 100644 index 0000000..c0a42b8 --- /dev/null +++ b/include/icsneo/icsneoc2types.h @@ -0,0 +1,382 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** @brief + * Options for opening a device. See icsneoc2_device_open() for more info. + */ +typedef enum _icsneoc2_open_options_t { + // No options + icsneoc2_open_options_none = 0x0, + // After opening, go online + icsneoc2_open_options_go_online = 0x1, + // After opening, enable message polling + icsneoc2_open_options_enable_message_polling = 0x2, + // After opening, sync RTC + icsneoc2_open_options_sync_rtc = 0x4, + // After opening, enable auto update + icsneoc2_open_options_enable_auto_update = 0x8, + // After opening, force update + icsneoc2_open_options_force_update = 0x10, +} _icsneoc2_open_options_t; + +/** @brief Integer representation of _icsneoc2_open_options_t enum. + * + * This is used for easier ABI compatibility, especially between other languages. + */ +typedef uint32_t icsneoc2_open_options_t; + + +/** @brief + * Intrepid hardware device types, useful for filtering out or identifying devices. +*/ +typedef enum _icsneoc2_devicetype_t { + // Unknown device type + icsneoc2_devicetype_unknown, + // neoVI Blue - Obsolete + icsneoc2_devicetype_blue, + // neoECU AVB/TSN + icsneoc2_devicetype_ecu_avb, + // RAD-SuperMoon + icsneoc2_devicetype_rad_supermoon, + // DualWire ValueCAN - Obsolete + icsneoc2_devicetype_dw_vcan, + // RAD-Moon 2 + icsneoc2_devicetype_rad_moon2, + // RAD-Mars + icsneoc2_devicetype_rad_mars, + // ValueCAN 4-1 + icsneoc2_devicetype_vcan41, + // neoVI FIRE + icsneoc2_devicetype_fire, + // RAD-Pluto + icsneoc2_devicetype_rad_pluto, + // ValueCAN 4-2EL + icsneoc2_devicetype_vcan42_el, + // RAD-IO CAN-HUB + icsneoc2_devicetype_radio_canhub, + // neoECU12 + icsneoc2_devicetype_neo_ecu12, + // neoOBD2-LC Badge + icsneoc2_devicetype_obd2_lc_badge, + // RAD-Moon Duo + icsneoc2_devicetype_rad_moon_duo, + // neoVI FIRE3 + icsneoc2_devicetype_fire3, + // ValueCAN3 + icsneoc2_devicetype_vcan3, + // RAD-Jupiter + icsneoc2_devicetype_rad_jupiter, + // ValueCAN4 Industrial + icsneoc2_devicetype_vcan4_industrial, + // RAD-Gigastar + icsneoc2_devicetype_rad_gigastar, + // neoVI RED2 + icsneoc2_devicetype_red2, + // EtherBADGE + icsneoc2_devicetype_etherbadge, + // RAD-A2B + icsneoc2_devicetype_rad_a2b, + // RAD-Epsilon + icsneoc2_devicetype_rad_epsilon, + // RAD-Moon 3 + icsneoc2_devicetype_rad_moon3, + // RAD-Comet + icsneoc2_devicetype_rad_comet, + // neoVI FIRE3 FlexRay + icsneoc2_devicetype_fire3_flexray, + // neoVI CONNECT + icsneoc2_devicetype_connect, + // RAD-Comet 3 + icsneoc2_devicetype_rad_comet3, + // RAD-Moon T1S + icsneoc2_devicetype_rad_moon_t1s, + // RAD-Gigastar 2 + icsneoc2_devicetype_rad_gigastar2, + // neoVI RED + icsneoc2_devicetype_red, + // neoECU - Obsolete + icsneoc2_devicetype_ecu, + // IEVB - Obsolete + icsneoc2_devicetype_ievb, + // Pendant - Obsolete + icsneoc2_devicetype_pendant, + // neoOBD2 Pro - Obsolete + icsneoc2_devicetype_obd2_pro, + // neoECU Chip - Obsolete + icsneoc2_devicetype_ecuchip_uart, + // neoVI PLASMA + icsneoc2_devicetype_plasma, + // neoAnalog - Obsolete + icsneoc2_devicetype_neo_analog, + // Obsolete + icsneoc2_devicetype_ct_obd, + // neoVI ION + icsneoc2_devicetype_ion, + // RAD-Star - Obsolete + icsneoc2_devicetype_rad_star, + // ValueCAN4-4 + icsneoc2_devicetype_vcan44, + // ValueCAN4-2 + icsneoc2_devicetype_vcan42, + // CMProbe - Obsolete + icsneoc2_devicetype_cm_probe, + // Ethernet EVB - Obsolete + icsneoc2_devicetype_eevb, + // ValueCAN.rf - Obsolete + icsneoc2_devicetype_vcan_rf, + // neoVI FIRE2 + icsneoc2_devicetype_fire2, + // neoVI FLEX - Obsolete + icsneoc2_devicetype_flex, + // RAD-Galaxy + icsneoc2_devicetype_rad_galaxy, + // RAD-Star 2 + icsneoc2_devicetype_rad_star2, + // VividCAN + icsneoc2_devicetype_vividcan, + // neoOBD2 SIM + icsneoc2_devicetype_obd2_sim, + // RAD-Galaxy 2 + icsneoc2_devicetype_rad_galaxy2, + + + // Must be last entry + icsneoc2_devicetype_maxsize, +} _icsneoc2_devicetype_t; + +/** @brief Integer representation of _icsneoc2_devicetype_t enum. + * + * This is used for easier ABI compatibility, especially between other languages. + */ +typedef uint32_t icsneoc2_devicetype_t; + +typedef enum _icsneoc2_msg_type_t { + icsneoc2_msg_type_device, + icsneoc2_msg_type_internal, + icsneoc2_msg_type_bus, + + icsneoc2_msg_type_maxsize, +} _icsneoc2_msg_type_t; + +/** @brief Integer representation of _icsneoc2_msg_type_t enum. + * + * This is used for easier ABI compatibility, especially between other languages. + */ +typedef uint32_t icsneoc2_msg_type_t; + + +/** @brief + * Bus message types, useful for filtering out or identifying Bus Messages. +*/ +typedef enum _icsneoc2_msg_bus_type_t { + icsneoc2_msg_bus_type_invalid = 0, + icsneoc2_msg_bus_type_internal = 1, // Used for statuses that don't actually need to be transferred to the client application + icsneoc2_msg_bus_type_can = 2, + icsneoc2_msg_bus_type_lin = 3, + icsneoc2_msg_bus_type_flexray = 4, + icsneoc2_msg_bus_type_most = 5, + icsneoc2_msg_bus_type_ethernet = 6, + icsneoc2_msg_bus_type_lsftcan = 7, + icsneoc2_msg_bus_type_swcan = 8, + icsneoc2_msg_bus_type_iso9141 = 9, + icsneoc2_msg_bus_type_i2c = 10, + icsneoc2_msg_bus_type_a2b = 11, + icsneoc2_msg_bus_type_spi = 12, + icsneoc2_msg_bus_type_mdio = 13, + + // Must be last entry + icsneoc2_msg_bus_type_maxsize, + + icsneoc2_msg_bus_type_any = 0xFE, // Never actually set as type, but used as flag for filtering + icsneoc2_msg_bus_type_other = 0xFF +} _icsneoc2_msg_bus_type_t; + +/** @brief Integer representation of _icsneoc2_msg_bus_type_t enum. + * + * This is used for easier ABI compatibility, especially between other languages. + */ +typedef uint8_t icsneoc2_msg_bus_type_t; + +typedef enum _icsneoc2_netid_t { + icsneoc2_netid_device = 0, + icsneoc2_netid_hscan = 1, + icsneoc2_netid_mscan = 2, + icsneoc2_netid_swcan = 3, + icsneoc2_netid_lsftcan = 4, + icsneoc2_netid_fordscp = 5, + icsneoc2_netid_j1708 = 6, + icsneoc2_netid_aux = 7, + icsneoc2_netid_j1850vpw = 8, + icsneoc2_netid_iso9141 = 9, + icsneoc2_netid_disk_data = 10, + icsneoc2_netid_main51 = 11, + icsneoc2_netid_red = 12, + icsneoc2_netid_sci = 13, + icsneoc2_netid_iso9141_2 = 14, + icsneoc2_netid_iso14230 = 15, + icsneoc2_netid_lin = 16, + icsneoc2_netid_op_ethernet1 = 17, + icsneoc2_netid_op_ethernet2 = 18, + icsneoc2_netid_op_ethernet3 = 19, + + // START Device Command Returns + // When we send a command, the device returns on one of these, depending on command + icsneoc2_netid_red_ext_memoryread = 20, + icsneoc2_netid_red_int_memoryread = 21, + icsneoc2_netid_red_dflash_read = 22, + icsneoc2_netid_neo_memory_sdread = 23, // Response from NeoMemory (MemoryTypeSD) + icsneoc2_netid_can_errbits = 24, + icsneoc2_netid_neo_memory_write_done = 25, + icsneoc2_netid_red_wave_can1_logical = 26, + icsneoc2_netid_red_wave_can2_logical = 27, + icsneoc2_netid_red_wave_lin1_logical = 28, + icsneoc2_netid_red_wave_lin2_logical = 29, + icsneoc2_netid_red_wave_lin1_analog = 30, + icsneoc2_netid_red_wave_lin2_analog = 31, + icsneoc2_netid_red_wave_misc_analog = 32, + icsneoc2_netid_red_wave_miscdio2_logical = 33, + icsneoc2_netid_red_network_com_enable_ex = 34, + icsneoc2_netid_red_neovi_network = 35, + icsneoc2_netid_red_read_baud_settings = 36, + icsneoc2_netid_red_oldformat = 37, + icsneoc2_netid_red_scope_capture = 38, + icsneoc2_netid_red_hardware_excep = 39, + icsneoc2_netid_red_get_rtc = 40, + // END Device Command Returns + + icsneoc2_netid_iso9141_3 = 41, + icsneoc2_netid_hscan2 = 42, + icsneoc2_netid_hscan3 = 44, + icsneoc2_netid_op_ethernet4 = 45, + icsneoc2_netid_op_ethernet5 = 46, + icsneoc2_netid_iso9141_4 = 47, + icsneoc2_netid_lin2 = 48, + icsneoc2_netid_lin3 = 49, + icsneoc2_netid_lin4 = 50, + icsneoc2_netid_most_unused = 51, // MOST = 51, Old and unused + icsneoc2_netid_red_app_error = 52, + icsneoc2_netid_cgi = 53, + icsneoc2_netid_reset_status = 54, + icsneoc2_netid_fb_status = 55, + icsneoc2_netid_app_signal_status = 56, + icsneoc2_netid_read_datalink_cm_tx_msg = 57, + icsneoc2_netid_read_datalink_cm_rx_msg = 58, + icsneoc2_netid_logging_overflow = 59, + icsneoc2_netid_read_settings = 60, + icsneoc2_netid_hscan4 = 61, + icsneoc2_netid_hscan5 = 62, + icsneoc2_netid_rs232 = 63, + icsneoc2_netid_uart = 64, + icsneoc2_netid_uart2 = 65, + icsneoc2_netid_uart3 = 66, + icsneoc2_netid_uart4 = 67, + icsneoc2_netid_swcan2 = 68, + icsneoc2_netid_ethernet_daq = 69, + icsneoc2_netid_data_to_host = 70, + icsneoc2_netid_textapi_to_host = 71, + icsneoc2_netid_spi1 = 72, + icsneoc2_netid_op_ethernet6 = 73, + icsneoc2_netid_red_vbat = 74, + icsneoc2_netid_op_ethernet7 = 75, + icsneoc2_netid_op_ethernet8 = 76, + icsneoc2_netid_op_ethernet9 = 77, + icsneoc2_netid_op_ethernet10 = 78, + icsneoc2_netid_op_ethernet11 = 79, + icsneoc2_netid_flexray1a = 80, + icsneoc2_netid_flexray1b = 81, + icsneoc2_netid_flexray2a = 82, + icsneoc2_netid_flexray2b = 83, + icsneoc2_netid_lin5 = 84, + icsneoc2_netid_flexray = 85, + icsneoc2_netid_flexray2 = 86, + icsneoc2_netid_op_ethernet12 = 87, + icsneoc2_netid_i2c = 88, + icsneoc2_netid_most25 = 90, + icsneoc2_netid_most50 = 91, + icsneoc2_netid_most150 = 92, + icsneoc2_netid_ethernet = 93, + icsneoc2_netid_gmfsa = 94, + icsneoc2_netid_tcp = 95, + icsneoc2_netid_hscan6 = 96, + icsneoc2_netid_hscan7 = 97, + icsneoc2_netid_lin6 = 98, + icsneoc2_netid_lsftcan2 = 99, + icsneoc2_netid_logical_disk_info = 187, + icsneoc2_netid_wivi_command = 221, + icsneoc2_netid_script_status = 224, + icsneoc2_netid_eth_phy_control = 239, + icsneoc2_netid_extended_command = 240, + icsneoc2_netid_extended_data = 242, + icsneoc2_netid_flexray_control = 243, + icsneoc2_netid_coremini_preload = 244, + icsneoc2_netid_hw_com_latency_test = 512, + icsneoc2_netid_device_status = 513, + icsneoc2_netid_udp = 514, + icsneoc2_netid_forwarded_message = 516, + icsneoc2_netid_i2c2 = 517, + icsneoc2_netid_i2c3 = 518, + icsneoc2_netid_i2c4 = 519, + icsneoc2_netid_ethernet2 = 520, + icsneoc2_netid_a2b1 = 522, + icsneoc2_netid_a2b2 = 523, + icsneoc2_netid_ethernet3 = 524, + icsneoc2_netid_wbms = 532, + icsneoc2_netid_dwcan9 = 534, + icsneoc2_netid_dwcan10 = 535, + icsneoc2_netid_dwcan11 = 536, + icsneoc2_netid_dwcan12 = 537, + icsneoc2_netid_dwcan13 = 538, + icsneoc2_netid_dwcan14 = 539, + icsneoc2_netid_dwcan15 = 540, + icsneoc2_netid_dwcan16 = 541, + icsneoc2_netid_lin7 = 542, + icsneoc2_netid_lin8 = 543, + icsneoc2_netid_spi2 = 544, + icsneoc2_netid_mdio1 = 545, + icsneoc2_netid_mdio2 = 546, + icsneoc2_netid_mdio3 = 547, + icsneoc2_netid_mdio4 = 548, + icsneoc2_netid_mdio5 = 549, + icsneoc2_netid_mdio6 = 550, + icsneoc2_netid_mdio7 = 551, + icsneoc2_netid_mdio8 = 552, + icsneoc2_netid_op_ethernet13 = 553, + icsneoc2_netid_op_ethernet14 = 554, + icsneoc2_netid_op_ethernet15 = 555, + icsneoc2_netid_op_ethernet16 = 556, + icsneoc2_netid_spi3 = 557, + icsneoc2_netid_spi4 = 558, + icsneoc2_netid_spi5 = 559, + icsneoc2_netid_spi6 = 560, + icsneoc2_netid_spi7 = 561, + icsneoc2_netid_spi8 = 562, + icsneoc2_netid_lin9 = 563, + icsneoc2_netid_lin10 = 564, + icsneoc2_netid_lin11 = 565, + icsneoc2_netid_lin12 = 566, + icsneoc2_netid_lin13 = 567, + icsneoc2_netid_lin14 = 568, + icsneoc2_netid_lin15 = 569, + icsneoc2_netid_lin16 = 570, + + // Must be the last entry + icsneoc2_netid_maxsize, + + icsneoc2_netid_any = 0xfffe, // Never actually set as type, but used as flag for filtering + icsneoc2_netid_invalid = 0xffff +} _icsneoc2_netid_t; + +/** @brief Integer representation of _icsneoc2_netid_t enum. + * + * This is used for easier ABI compatibility, especially between other languages. + */ +typedef uint16_t icsneoc2_netid_t; + +#ifdef __cplusplus +} +#endif diff --git a/test/unit/icsneoc2.cpp b/test/unit/icsneoc2.cpp new file mode 100644 index 0000000..b8a93da --- /dev/null +++ b/test/unit/icsneoc2.cpp @@ -0,0 +1,453 @@ +#include +#include + +#include +#include + +TEST(icsneoc2, test_icsneoc2_device_find_all) { + const auto MAX_DEV_SIZE = 255; + icsneoc2_device_t* devices[MAX_DEV_SIZE] = {0}; + uint32_t devices_count = MAX_DEV_SIZE; + + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_find_all(NULL, NULL, NULL)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_find_all(devices, NULL, NULL)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_find_all(NULL, &devices_count, NULL)); + ASSERT_EQ(icsneoc2_error_success, icsneoc2_device_find_all(devices, &devices_count, NULL)); + for (uint32_t i=0; i < MAX_DEV_SIZE; ++i) { + if (i < devices_count) { + ASSERT_EQ(icsneoc2_error_success, icsneoc2_device_is_valid(devices[i])); + } else { + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_is_valid(devices[i])); + } + } +} + +TEST(icsneoc2, test_icsneoc2_is_device) { + icsneoc2_device_t* invalid_device = static_cast((void*)0x1234); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_is_valid(NULL)); + ASSERT_EQ(icsneoc2_error_invalid_device, icsneoc2_device_is_valid(invalid_device)); +} + +TEST(icsneoc2, test_icsneoc2_error_invalid_parameters_and_invalid_device) { + bool placeholderBool = false; + uint32_t placeholderInteger32 = 0; + uint64_t placeholderInteger64 = 0; + const char placeholderStr[255] = {0}; + icsneoc2_event_t* eventPlaceHolder = nullptr; + + // All of these don't have a device parameter + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_find_all(NULL, NULL, NULL)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_bus_type_name_get(0, NULL, NULL)); + + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_event_description_get(eventPlaceHolder, placeholderStr, &placeholderInteger32)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_events_get(NULL, &placeholderInteger32)); + + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_device_type_name_get(0, NULL, NULL)); + + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_error_code_get(0, NULL, NULL)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_message_type_name_get(0, NULL, NULL)); + ASSERT_EQ(icsneoc2_error_invalid_parameters, icsneoc2_netid_name_get(0, NULL, NULL)); + + // Test everything with a device and with a device with a bad pointer + std::vector> testPatterns = { + { icsneoc2_error_invalid_parameters, NULL}, + { icsneoc2_error_invalid_device, static_cast((void*)0x1234)}, + }; + for (const auto& testPattern : testPatterns) { + const auto& errorCode = std::get<0>(testPattern); + const auto& device = std::get<1>(testPattern); + + ASSERT_EQ(errorCode, icsneoc2_device_baudrate_get(device, 0, &placeholderInteger64)); + ASSERT_EQ(errorCode, icsneoc2_device_baudrate_set(device, 0, 0, placeholderInteger64)); + ASSERT_EQ(errorCode, icsneoc2_device_canfd_baudrate_get(device, 0, &placeholderInteger64)); + ASSERT_EQ(errorCode, icsneoc2_device_canfd_baudrate_set(device, 0, 0, placeholderInteger64)); + ASSERT_EQ(errorCode, icsneoc2_device_close(device)); + ASSERT_EQ(errorCode, icsneoc2_device_description_get(device, placeholderStr, &placeholderInteger32)); + + ASSERT_EQ(errorCode, icsneoc2_device_go_online(device, false)); + ASSERT_EQ(errorCode, icsneoc2_device_is_online(device, &placeholderBool)); + ASSERT_EQ(errorCode, icsneoc2_device_is_online_supported(device, &placeholderBool)); + ASSERT_EQ(errorCode, icsneoc2_device_is_valid(device)); + ASSERT_EQ(errorCode, icsneoc2_device_is_open(device, &placeholderBool)); + ASSERT_EQ(errorCode, icsneoc2_device_is_disconnected(device, &placeholderBool)); + ASSERT_EQ(errorCode, icsneoc2_device_load_default_settings(device, false)); + + ASSERT_EQ(errorCode, icsneoc2_device_open(device)); + ASSERT_EQ(errorCode, icsneoc2_device_open_options_get(device, &placeholderInteger32)); + ASSERT_EQ(errorCode, icsneoc2_device_open_options_set(device, 0)); + ASSERT_EQ(errorCode, icsneoc2_device_rtc_get(device, (int64_t*)&placeholderInteger64)); + ASSERT_EQ(errorCode, icsneoc2_device_rtc_set(device, 0)); + ASSERT_EQ(errorCode, icsneoc2_device_serial_get(device, placeholderStr, &placeholderInteger32)); + ASSERT_EQ(errorCode, icsneoc2_device_supports_tc10(device, &placeholderBool)); + ASSERT_EQ(errorCode, icsneoc2_device_timestamp_resolution_get(device, &placeholderInteger32)); + + ASSERT_EQ(errorCode, icsneoc2_device_type_get(device, NULL)); + + ASSERT_EQ(errorCode, icsneoc2_device_message_count_get(device, &placeholderInteger32)); + ASSERT_EQ(errorCode, icsneoc2_device_message_polling_get(device, &placeholderBool)); + ASSERT_EQ(errorCode, icsneoc2_device_message_polling_limit_get(device, &placeholderInteger32)); + ASSERT_EQ(errorCode, icsneoc2_device_message_polling_set(device, false)); + ASSERT_EQ(errorCode, icsneoc2_device_message_polling_set_limit(device, 0)); + ASSERT_EQ(errorCode, icsneoc2_device_messages_get(device, NULL, NULL, 0)); + ASSERT_EQ(errorCode, icsneoc2_device_messages_transmit(device, NULL, NULL)); + + ASSERT_EQ(errorCode, icsneoc2_message_bus_type_get(device, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_can_arbid_get(device, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_can_arbid_set(device, NULL, 0)); + ASSERT_EQ(errorCode, icsneoc2_message_can_baudrate_switch_get(device, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_can_baudrate_switch_set(device, NULL, false)); + ASSERT_EQ(errorCode, icsneoc2_message_can_canfd_set(device, NULL, false)); + ASSERT_EQ(errorCode, icsneoc2_message_can_create(device, NULL, 0)); + ASSERT_EQ(errorCode, icsneoc2_message_can_dlc_get(device, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_can_dlc_set(device, NULL, 0)); + ASSERT_EQ(errorCode, icsneoc2_message_can_error_state_indicator_get(device, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_can_extended_set(device, NULL, false)); + ASSERT_EQ(errorCode, icsneoc2_message_can_free(device, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_can_is_canfd(device, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_can_is_extended(device, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_can_is_remote(device, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_data_get(device, NULL, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_data_set(device, NULL, NULL, 0)); + ASSERT_EQ(errorCode, icsneoc2_message_is_transmit(device, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_is_valid(device, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_netid_get(device, NULL, NULL)); + ASSERT_EQ(errorCode, icsneoc2_message_netid_set(device, NULL, 0)); + ASSERT_EQ(errorCode, icsneoc2_message_type_get(device, NULL, NULL)); + + ASSERT_EQ(errorCode, icsneoc2_device_events_get(device, NULL, NULL)); + } +} + +TEST(icsneoc2, test_icsneoc2_devicetype_t) { + std::vector<_icsneoc2_devicetype_t> devicetypeValues = { + // Unknown device type + icsneoc2_devicetype_unknown, + // neoVI Blue - Obsolete + icsneoc2_devicetype_blue, + // neoECU AVB/TSN + icsneoc2_devicetype_ecu_avb, + // RAD-SuperMoon + icsneoc2_devicetype_rad_supermoon, + // DualWire ValueCAN - Obsolete + icsneoc2_devicetype_dw_vcan, + // RAD-Moon 2 + icsneoc2_devicetype_rad_moon2, + // RAD-Mars + icsneoc2_devicetype_rad_mars, + // ValueCAN 4-1 + icsneoc2_devicetype_vcan41, + // neoVI FIRE + icsneoc2_devicetype_fire, + // RAD-Pluto + icsneoc2_devicetype_rad_pluto, + // ValueCAN 4-2EL + icsneoc2_devicetype_vcan42_el, + // RAD-IO CAN-HUB + icsneoc2_devicetype_radio_canhub, + // neoECU12 + icsneoc2_devicetype_neo_ecu12, + // neoOBD2-LC Badge + icsneoc2_devicetype_obd2_lc_badge, + // RAD-Moon Duo + icsneoc2_devicetype_rad_moon_duo, + // neoVI FIRE3 + icsneoc2_devicetype_fire3, + // ValueCAN3 + icsneoc2_devicetype_vcan3, + // RAD-Jupiter + icsneoc2_devicetype_rad_jupiter, + // ValueCAN4 Industrial + icsneoc2_devicetype_vcan4_industrial, + // RAD-Gigastar + icsneoc2_devicetype_rad_gigastar, + // neoVI RED2 + icsneoc2_devicetype_red2, + // EtherBADGE + icsneoc2_devicetype_etherbadge, + // RAD-A2B + icsneoc2_devicetype_rad_a2b, + // RAD-Epsilon + icsneoc2_devicetype_rad_epsilon, + // RAD-Moon 3 + icsneoc2_devicetype_rad_moon3, + // RAD-Comet + icsneoc2_devicetype_rad_comet, + // neoVI FIRE3 FlexRay + icsneoc2_devicetype_fire3_flexray, + // neoVI CONNECT + icsneoc2_devicetype_connect, + // RAD-Comet 3 + icsneoc2_devicetype_rad_comet3, + // RAD-Moon T1S + icsneoc2_devicetype_rad_moon_t1s, + // RAD-Gigastar 2 + icsneoc2_devicetype_rad_gigastar2, + // neoVI RED + icsneoc2_devicetype_red, + // neoECU - Obsolete + icsneoc2_devicetype_ecu, + // IEVB - Obsolete + icsneoc2_devicetype_ievb, + // Pendant - Obsolete + icsneoc2_devicetype_pendant, + // neoOBD2 Pro - Obsolete + icsneoc2_devicetype_obd2_pro, + // neoECU Chip - Obsolete + icsneoc2_devicetype_ecuchip_uart, + // neoVI PLASMA + icsneoc2_devicetype_plasma, + // neoAnalog - Obsolete + icsneoc2_devicetype_neo_analog, + // Obsolete + icsneoc2_devicetype_ct_obd, + // neoVI ION + icsneoc2_devicetype_ion, + // RAD-Star - Obsolete + icsneoc2_devicetype_rad_star, + // ValueCAN4-4 + icsneoc2_devicetype_vcan44, + // ValueCAN4-2 + icsneoc2_devicetype_vcan42, + // CMProbe - Obsolete + icsneoc2_devicetype_cm_probe, + // Ethernet EVB - Obsolete + icsneoc2_devicetype_eevb, + // ValueCAN.rf - Obsolete + icsneoc2_devicetype_vcan_rf, + // neoVI FIRE2 + icsneoc2_devicetype_fire2, + // neoVI FLEX - Obsolete + icsneoc2_devicetype_flex, + // RAD-Galaxy + icsneoc2_devicetype_rad_galaxy, + // RAD-Star 2 + icsneoc2_devicetype_rad_star2, + // VividCAN + icsneoc2_devicetype_vividcan, + // neoOBD2 SIM + icsneoc2_devicetype_obd2_sim, + // RAD-Galaxy 2 + icsneoc2_devicetype_rad_galaxy2, + + + // Must be last entry + icsneoc2_devicetype_maxsize, + }; + auto i = 0; + for (const auto& devicetypeValue : devicetypeValues) { + ASSERT_EQ(devicetypeValue, i++); + } + + ASSERT_EQ(sizeof(icsneoc2_devicetype_t), sizeof(uint32_t)); +} + +TEST(icsneoc2, test_icsneoc2_msg_type_t) { + std::vector<_icsneoc2_msg_type_t> msgtypeValues = { + icsneoc2_msg_type_device, + icsneoc2_msg_type_internal, + icsneoc2_msg_type_bus, + + icsneoc2_msg_type_maxsize, + }; + auto i = 0; + for (const auto& msgtypeValue : msgtypeValues) { + ASSERT_EQ(msgtypeValue, i++); + } + + ASSERT_EQ(sizeof(icsneoc2_msg_type_t), sizeof(uint32_t)); +} + +TEST(icsneoc2, test_icsneoc2_msg_bus_type_t) { + std::vector<_icsneoc2_msg_bus_type_t> msgbustypeValues = { + icsneoc2_msg_bus_type_invalid, + icsneoc2_msg_bus_type_internal, // Used for statuses that don't actually need to be transferred to the client application + icsneoc2_msg_bus_type_can, + icsneoc2_msg_bus_type_lin, + icsneoc2_msg_bus_type_flexray, + icsneoc2_msg_bus_type_most, + icsneoc2_msg_bus_type_ethernet, + icsneoc2_msg_bus_type_lsftcan, + icsneoc2_msg_bus_type_swcan, + icsneoc2_msg_bus_type_iso9141, + icsneoc2_msg_bus_type_i2c, + icsneoc2_msg_bus_type_a2b, + icsneoc2_msg_bus_type_spi, + icsneoc2_msg_bus_type_mdio, + + // Must be last entry + icsneoc2_msg_bus_type_maxsize, + }; + auto i = 0; + for (const auto& msgbustypeValue : msgbustypeValues) { + ASSERT_EQ(msgbustypeValue, i++); + } + + ASSERT_EQ(sizeof(icsneoc2_msg_bus_type_t), sizeof(uint8_t)); +} + +TEST(icsneoc2, test_icsneoc2_netid_t) { + std::vector> netidValues = { + { icsneoc2_netid_device, 0 }, + { icsneoc2_netid_hscan, 1 }, + { icsneoc2_netid_mscan, 2 }, + { icsneoc2_netid_swcan, 3 }, + { icsneoc2_netid_lsftcan, 4 }, + { icsneoc2_netid_fordscp, 5 }, + { icsneoc2_netid_j1708, 6 }, + { icsneoc2_netid_aux, 7 }, + { icsneoc2_netid_j1850vpw, 8 }, + { icsneoc2_netid_iso9141, 9 }, + { icsneoc2_netid_disk_data, 10 }, + { icsneoc2_netid_main51, 11 }, + { icsneoc2_netid_red, 12 }, + { icsneoc2_netid_sci, 13 }, + { icsneoc2_netid_iso9141_2, 14 }, + { icsneoc2_netid_iso14230, 15 }, + { icsneoc2_netid_lin, 16 }, + { icsneoc2_netid_op_ethernet1, 17 }, + { icsneoc2_netid_op_ethernet2, 18 }, + { icsneoc2_netid_op_ethernet3, 19 }, + { icsneoc2_netid_red_ext_memoryread, 20 }, + { icsneoc2_netid_red_int_memoryread, 21 }, + { icsneoc2_netid_red_dflash_read, 22 }, + { icsneoc2_netid_neo_memory_sdread, 23 }, // Response from NeoMemory (MemoryTypeSD) + { icsneoc2_netid_can_errbits, 24 }, + { icsneoc2_netid_neo_memory_write_done, 25 }, + { icsneoc2_netid_red_wave_can1_logical, 26 }, + { icsneoc2_netid_red_wave_can2_logical, 27 }, + { icsneoc2_netid_red_wave_lin1_logical, 28 }, + { icsneoc2_netid_red_wave_lin2_logical, 29 }, + { icsneoc2_netid_red_wave_lin1_analog, 30 }, + { icsneoc2_netid_red_wave_lin2_analog, 31 }, + { icsneoc2_netid_red_wave_misc_analog, 32 }, + { icsneoc2_netid_red_wave_miscdio2_logical, 33 }, + { icsneoc2_netid_red_network_com_enable_ex, 34 }, + { icsneoc2_netid_red_neovi_network, 35 }, + { icsneoc2_netid_red_read_baud_settings, 36 }, + { icsneoc2_netid_red_oldformat, 37 }, + { icsneoc2_netid_red_scope_capture, 38 }, + { icsneoc2_netid_red_hardware_excep, 39 }, + { icsneoc2_netid_red_get_rtc, 40 }, + { icsneoc2_netid_iso9141_3, 41 }, + { icsneoc2_netid_hscan2, 42 }, + { icsneoc2_netid_hscan3, 44 }, + { icsneoc2_netid_op_ethernet4, 45 }, + { icsneoc2_netid_op_ethernet5, 46 }, + { icsneoc2_netid_iso9141_4, 47 }, + { icsneoc2_netid_lin2, 48 }, + { icsneoc2_netid_lin3, 49 }, + { icsneoc2_netid_lin4, 50 }, + { icsneoc2_netid_most_unused, 51 }, // MOST = 51, Old and unused + { icsneoc2_netid_red_app_error, 52 }, + { icsneoc2_netid_cgi, 53 }, + { icsneoc2_netid_reset_status, 54 }, + { icsneoc2_netid_fb_status, 55 }, + { icsneoc2_netid_app_signal_status, 56 }, + { icsneoc2_netid_read_datalink_cm_tx_msg, 57 }, + { icsneoc2_netid_read_datalink_cm_rx_msg, 58 }, + { icsneoc2_netid_logging_overflow, 59 }, + { icsneoc2_netid_read_settings, 60 }, + { icsneoc2_netid_hscan4, 61 }, + { icsneoc2_netid_hscan5, 62 }, + { icsneoc2_netid_rs232, 63 }, + { icsneoc2_netid_uart, 64 }, + { icsneoc2_netid_uart2, 65 }, + { icsneoc2_netid_uart3, 66 }, + { icsneoc2_netid_uart4, 67 }, + { icsneoc2_netid_swcan2, 68 }, + { icsneoc2_netid_ethernet_daq, 69 }, + { icsneoc2_netid_data_to_host, 70 }, + { icsneoc2_netid_textapi_to_host, 71 }, + { icsneoc2_netid_spi1, 72 }, + { icsneoc2_netid_op_ethernet6, 73 }, + { icsneoc2_netid_red_vbat, 74 }, + { icsneoc2_netid_op_ethernet7, 75 }, + { icsneoc2_netid_op_ethernet8, 76 }, + { icsneoc2_netid_op_ethernet9, 77 }, + { icsneoc2_netid_op_ethernet10, 78 }, + { icsneoc2_netid_op_ethernet11, 79 }, + { icsneoc2_netid_flexray1a, 80 }, + { icsneoc2_netid_flexray1b, 81 }, + { icsneoc2_netid_flexray2a, 82 }, + { icsneoc2_netid_flexray2b, 83 }, + { icsneoc2_netid_lin5, 84 }, + { icsneoc2_netid_flexray, 85 }, + { icsneoc2_netid_flexray2, 86 }, + { icsneoc2_netid_op_ethernet12, 87 }, + { icsneoc2_netid_i2c, 88 }, + { icsneoc2_netid_most25, 90 }, + { icsneoc2_netid_most50, 91 }, + { icsneoc2_netid_most150, 92 }, + { icsneoc2_netid_ethernet, 93 }, + { icsneoc2_netid_gmfsa, 94 }, + { icsneoc2_netid_tcp, 95 }, + { icsneoc2_netid_hscan6, 96 }, + { icsneoc2_netid_hscan7, 97 }, + { icsneoc2_netid_lin6, 98 }, + { icsneoc2_netid_lsftcan2, 99 }, + { icsneoc2_netid_logical_disk_info, 187 }, + { icsneoc2_netid_wivi_command, 221 }, + { icsneoc2_netid_script_status, 224 }, + { icsneoc2_netid_eth_phy_control, 239 }, + { icsneoc2_netid_extended_command, 240 }, + { icsneoc2_netid_extended_data, 242 }, + { icsneoc2_netid_flexray_control, 243 }, + { icsneoc2_netid_coremini_preload, 244 }, + { icsneoc2_netid_hw_com_latency_test, 512 }, + { icsneoc2_netid_device_status, 513 }, + { icsneoc2_netid_udp, 514 }, + { icsneoc2_netid_forwarded_message, 516 }, + { icsneoc2_netid_i2c2, 517 }, + { icsneoc2_netid_i2c3, 518 }, + { icsneoc2_netid_i2c4, 519 }, + { icsneoc2_netid_ethernet2, 520 }, + { icsneoc2_netid_a2b1, 522 }, + { icsneoc2_netid_a2b2, 523 }, + { icsneoc2_netid_ethernet3, 524 }, + { icsneoc2_netid_wbms, 532 }, + { icsneoc2_netid_dwcan9, 534 }, + { icsneoc2_netid_dwcan10, 535 }, + { icsneoc2_netid_dwcan11, 536 }, + { icsneoc2_netid_dwcan12, 537 }, + { icsneoc2_netid_dwcan13, 538 }, + { icsneoc2_netid_dwcan14, 539 }, + { icsneoc2_netid_dwcan15, 540 }, + { icsneoc2_netid_dwcan16, 541 }, + { icsneoc2_netid_lin7, 542 }, + { icsneoc2_netid_lin8, 543 }, + { icsneoc2_netid_spi2, 544 }, + { icsneoc2_netid_mdio1, 545 }, + { icsneoc2_netid_mdio2, 546 }, + { icsneoc2_netid_mdio3, 547 }, + { icsneoc2_netid_mdio4, 548 }, + { icsneoc2_netid_mdio5, 549 }, + { icsneoc2_netid_mdio6, 550 }, + { icsneoc2_netid_mdio7, 551 }, + { icsneoc2_netid_mdio8, 552 }, + { icsneoc2_netid_op_ethernet13, 553 }, + { icsneoc2_netid_op_ethernet14, 554 }, + { icsneoc2_netid_op_ethernet15, 555 }, + { icsneoc2_netid_op_ethernet16, 556 }, + { icsneoc2_netid_spi3, 557 }, + { icsneoc2_netid_spi4, 558 }, + { icsneoc2_netid_spi5, 559 }, + { icsneoc2_netid_spi6, 560 }, + { icsneoc2_netid_spi7, 561 }, + { icsneoc2_netid_spi8, 562 }, + { icsneoc2_netid_lin9, 563 }, + { icsneoc2_netid_lin10, 564 }, + { icsneoc2_netid_lin11, 565 }, + { icsneoc2_netid_lin12, 566 }, + { icsneoc2_netid_lin13, 567 }, + { icsneoc2_netid_lin14, 568 }, + { icsneoc2_netid_lin15, 569 }, + { icsneoc2_netid_lin16, 570 }, + { icsneoc2_netid_maxsize, 571 } + }; + + for (const auto& netidValue : netidValues) { + const _icsneoc2_netid_t actualValue = std::get<0>(netidValue); + const icsneoc2_netid_t expectedValue = std::get<1>(netidValue); + ASSERT_EQ(actualValue, expectedValue); + } +} \ No newline at end of file