#include #include #include #ifdef _WIN32 #include #else #include #endif #include #include #include /** * 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) { #ifdef _WIN32 Sleep(ms); #else sleep(ms / 1000); #endif } /** * 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[64]; size_t error_str_len = sizeof(error_str); icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len); if(res != icsneoc2_error_success) { printf("%s: Failed to get string for error code %d with error code %d\n", message, error, res); return res; } printf("%s: \"%s\" (%u)\n", message, error_str, error); return (int)error; } /** * 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 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_message_t** messages, size_t messages_count); /** * Prints all events * * @param device_description A description of the device used in the output. */ void print_events(const char* device_description); /** * 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); /** * 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); int main() { icsneoc2_device_info_t* found_devices = NULL; printf("Finding devices...\n"); icsneoc2_error_t res = icsneoc2_device_enumerate(0, &found_devices); if(res != icsneoc2_error_success) { return print_error_code("\tFailed to find devices", res); } if(found_devices == NULL) { printf("No devices found, exiting\n"); return 0; } // Count and list off the devices size_t devices_count = 0; for(icsneoc2_device_info_t* cur = found_devices; cur != NULL; cur = icsneoc2_device_info_next(cur)) { devices_count++; } printf("OK, %zu device%s found\n", devices_count, devices_count == 1 ? "" : "s"); for(icsneoc2_device_info_t* cur = found_devices; cur != NULL; cur = icsneoc2_device_info_next(cur)) { // Get description of the device char description[255] = {0}; size_t description_length = 255; res = icsneoc2_device_info_description_get(cur, description, &description_length); if(res != icsneoc2_error_success) { icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to get device description", res); }; printf("%.*s\n", (int)description_length, description); // Open the device without RTC sync and going online icsneoc2_open_options_t options = icsneoc2_open_options_default; options &= ~ICSNEOC2_OPEN_OPTIONS_SYNC_RTC; options &= ~ICSNEOC2_OPEN_OPTIONS_GO_ONLINE; printf("\tDevice open options: 0x%x\n", options); printf("\tOpening device: %s...\n", description); icsneoc2_device_t* open_device = NULL; res = icsneoc2_device_create(cur, &open_device); if(res != icsneoc2_error_success) { icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to create device", res); } res = icsneoc2_device_open(open_device, options); if(res != icsneoc2_error_success) { icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to open device", res); }; // Get timestamp resolution of the device printf("\tGetting timestamp resolution... "); uint32_t timestamp_resolution = 0; res = icsneoc2_device_timestamp_resolution_get(open_device, ×tamp_resolution); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to get timestamp resolution", res); } printf("%uns\n", timestamp_resolution); // Get baudrates for HSCAN printf("\tGetting DW CAN 01 Baudrate... "); int64_t baudrate = 0; res = icsneoc2_settings_baudrate_get(open_device, icsneoc2_netid_dwcan_01, &baudrate); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to get baudrate", res); }; printf("%" PRIu64 "mbit/s\n", baudrate); // Get FDbaudrates for HSCAN printf("\tGetting FD DW CAN 01 Baudrate... "); int64_t fd_baudrate = 0; res = icsneoc2_settings_canfd_baudrate_get(open_device, icsneoc2_netid_dwcan_01, &fd_baudrate); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to get FD baudrate", res); }; printf("%" PRIu64 "mbit/s\n", fd_baudrate); // 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 printf("\tSetting DW CAN 01 Baudrate... "); res = icsneoc2_settings_baudrate_set(open_device, icsneoc2_netid_dwcan_01, baudrate); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to set baudrate", res); }; printf("Ok\n"); // Set FDbaudrates for HSCAN printf("\tSetting FD DW CAN 01 Baudrate... "); res = icsneoc2_settings_canfd_baudrate_set(open_device, icsneoc2_netid_dwcan_01, fd_baudrate); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to set FD baudrate", res); }; printf("Ok\n"); // Get RTC printf("\tGetting RTC... "); res = get_and_print_rtc(open_device); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to get RTC", res); } // Set RTC printf("\tSetting RTC to current time... "); time_t current_time = time(NULL); res = icsneoc2_device_rtc_set(open_device, current_time); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to set RTC", res); } printf("Ok\n"); // Get RTC printf("\tGetting RTC... "); res = get_and_print_rtc(open_device); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to get RTC", res); } // Go online, start acking traffic printf("\tGoing online... "); res = icsneoc2_device_go_online(open_device, true); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to go online", res); } // Redundant check to show how to check if the device is online, if the previous // icsneoc2_device_go_online call was successful we can assume we are online already bool is_online = false; res = icsneoc2_device_is_online(open_device, &is_online); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to check if online", res); } printf("%s\n", is_online ? "Online" : "Offline"); // Transmit CAN messages res = transmit_can_messages(open_device); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to transmit CAN messages", res); } // Wait for the bus to collect some messages, requires an active bus to get messages printf("\tWaiting 1 second for messages...\n"); sleep_ms(1000); // Get the messages icsneoc2_message_t* messages[20000] = {0}; size_t message_count = 20000; printf("\tGetting messages from device with timeout of 3000ms on %s...\n", description); for(size_t i = 0; i < message_count; ++i) { res = icsneoc2_device_message_get(open_device, &messages[i], 0); if(res != icsneoc2_error_success) { for(size_t j = 0; j < i; ++j) { icsneoc2_message_free(messages[j]); } print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to get messages from device", res); }; if(messages[i] == NULL) { // no more messages message_count = i; break; } } // Process the messages res = process_messages(messages, message_count); if(res != icsneoc2_error_success) { for(size_t i = 0; i < message_count; ++i) { icsneoc2_message_free(messages[i]); } print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to process messages", res); } for(size_t i = 0; i < message_count; ++i) { icsneoc2_message_free(messages[i]); } // Finally, close the device. printf("\tClosing device: %s...\n", description); res = icsneoc2_device_close(open_device); if(res != icsneoc2_error_success) { print_events(description); icsneoc2_device_free(open_device); icsneoc2_enumeration_free(found_devices); return print_error_code("\tFailed to close device", res); }; // Print device events print_events(description); icsneoc2_device_free(open_device); } icsneoc2_enumeration_free(found_devices); printf("\n"); return 0; } icsneoc2_error_t get_and_print_rtc(icsneoc2_device_t* device) { 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", (long long)unix_epoch, rtc_time); return icsneoc2_error_success; } void print_events(const char* device_description) { icsneoc2_event_t* events[1024] = {0}; size_t events_count = 1024; for(size_t i = 0; i < events_count; ++i) { // no device filter, get all events icsneoc2_error_t res = icsneoc2_event_get(&events[i], NULL); if(res != icsneoc2_error_success) { for(size_t j = 0; j < i; ++j) { icsneoc2_event_free(events[j]); } (void)print_error_code("\tFailed to get device events", res); return; } if(events[i] == NULL) { events_count = i; break; } } // Loop over each event and describe it. for(size_t i = 0; i < events_count; i++) { char event_description[255] = {0}; size_t event_description_length = 255; icsneoc2_error_t 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 %zu: %s\n", device_description, i, event_description); } for(size_t i = 0; i < events_count; i++) { icsneoc2_event_free(events[i]); } printf("\t%s: Received %zu events\n", device_description, events_count); } int process_messages(icsneoc2_message_t** messages, size_t messages_count) { // Print the type and bus type of each message size_t tx_count = 0; size_t can_error_count = 0; for(size_t i = 0; i < messages_count; i++) { icsneoc2_message_t* message = messages[i]; // Check for CAN error messages bool is_can_error = false; icsneoc2_error_t res = icsneoc2_message_is_can_error(message, &is_can_error); if(res != icsneoc2_error_success) { return print_error_code("\tFailed to check if message is a CAN error", res); } if(is_can_error) { uint8_t tec = 0; uint8_t rec = 0; icsneoc2_can_error_code_t error_code = 0; icsneoc2_can_error_code_t data_error_code = 0; icsneoc2_message_can_error_flags_t error_flags = 0; icsneoc2_netid_t netid = 0; res = icsneoc2_message_netid_get(message, &netid); res += icsneoc2_message_can_error_props_get(message, &tec, &rec, &error_code, &data_error_code, &error_flags); if(res != icsneoc2_error_success) { return print_error_code("\tFailed to get CAN error properties", res); } char netid_name[128] = {0}; size_t netid_name_length = 128; icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); printf("\t%zd) CAN Error on %s (0x%x): TEC=%u REC=%u ErrorCode=%u DataErrorCode=%u%s%s%s\n", i, netid_name, netid, tec, rec, error_code, data_error_code, (error_flags & ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_BUS_OFF) ? " [BusOff]" : "", (error_flags & ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_PASSIVE) ? " [ErrorPassive]" : "", (error_flags & ICSNEOC2_MESSAGE_CAN_ERROR_FLAGS_ERROR_WARN) ? " [ErrorWarn]" : ""); can_error_count++; continue; } bool is_frame = false; res = icsneoc2_message_is_frame(message, &is_frame); if(res != icsneoc2_error_success) { return print_error_code("\tFailed to check if message is a frame", res); } if(!is_frame) { printf("Ignoring non-frame message at index %zu\n", i); continue; } icsneoc2_network_type_t network_type; res = icsneoc2_message_network_type_get(message, &network_type); if(res != icsneoc2_error_success) { return print_error_code("\tFailed to get message network type", res); } char network_type_name[128] = {0}; size_t network_type_name_length = 128; res = icsneoc2_network_type_name_get(network_type, network_type_name, &network_type_name_length); if(res != icsneoc2_error_success) { return print_error_code("\tFailed to get network type name", res); } bool is_tx = false; res = icsneoc2_message_is_transmit(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%zd) network type: %s (%u)\n", i, network_type_name, network_type); if(network_type == icsneoc2_network_type_can) { uint64_t timestamp = 0; uint64_t arbid = 0; int32_t dlc = 0; icsneoc2_netid_t netid = 0; icsneoc2_message_can_flags_t can_flags = 0; uint8_t data[64] = {0}; size_t data_length = 64; char netid_name[128] = {0}; size_t netid_name_length = 128; icsneoc2_error_t result = icsneoc2_message_netid_get(message, &netid); result += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); result += icsneoc2_message_timestamp_get(message, ×tamp); result += icsneoc2_message_can_props_get(message, &arbid, &can_flags); result += icsneoc2_message_data_get(message, data, &data_length); if(result != icsneoc2_error_success) { printf("\tFailed get get CAN parameters (error: %u) for index %zu\n", result, i); continue; } bool is_remote = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_RTR) != 0; bool is_extended = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_IDE) != 0; bool is_canfd = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_FDF) != 0; bool is_brs = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_BRS) != 0; bool is_esi = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_ESI) != 0; bool tx_aborted = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ABORTED) != 0; bool tx_lost_arb = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_TX_LOST_ARB) != 0; bool tx_error = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ERROR) != 0; dlc = (int32_t)data_length; printf("\t Timestamp: %" PRIu64 " ns since 2007-01-01 UTC\n", timestamp); printf("\t NetID: %s (0x%x)\tArbID: 0x%llx\tDLC: %u\tLen: %zu\n", netid_name, netid, (unsigned long long)arbid, dlc, data_length); printf("\t Flags:%s%s%s%s%s%s%s%s\n", is_remote ? " RTR" : "", is_extended ? " IDE" : "", is_canfd ? " FDF" : "", is_brs ? " BRS" : "", is_esi ? " ESI" : "", tx_aborted ? " TX_ABORTED" : "", tx_lost_arb ? " TX_LOST_ARB" : "", tx_error ? " TX_ERROR" : ""); printf("\t Data: ["); for(size_t x = 0; x < data_length; x++) { printf(" 0x%x", data[x]); } printf(" ]\n"); } } printf("\tReceived %zu messages total, %zu were TX messages, %zu were CAN errors\n", messages_count, tx_count, can_error_count); return icsneoc2_error_success; } int transmit_can_messages(icsneoc2_device_t* device) { uint64_t counter = 0; const size_t msg_count = 100; printf("\tTransmitting %zd messages...\n", msg_count); for(size_t i = 0; i < msg_count; i++) { // Create the message icsneoc2_message_t* message = NULL; icsneoc2_error_t res = icsneoc2_message_can_create(&message); if(res != icsneoc2_error_success) { return print_error_code("\tFailed to create messages", res); } // Set the message attributes res = icsneoc2_message_netid_set(message, icsneoc2_netid_dwcan_01); uint64_t arb_id = 0x10; uint64_t flags = ICSNEOC2_MESSAGE_CAN_FLAGS_BRS | ICSNEOC2_MESSAGE_CAN_FLAGS_IDE | ICSNEOC2_MESSAGE_CAN_FLAGS_FDF; res += icsneoc2_message_can_props_set(message, &arb_id, &flags); res += icsneoc2_message_data_set(message, (uint8_t*)&counter, sizeof(counter)); if(res != icsneoc2_error_success) { icsneoc2_message_free(message); return print_error_code("\tFailed to modify message", res); } res = icsneoc2_device_message_transmit(device, message); res += icsneoc2_message_free(message); if(res != icsneoc2_error_success) { return print_error_code("\tFailed to transmit message", res); } counter++; } return icsneoc2_error_success; }