API: Added icsneoc2.

Signed-off-by: David Rebbe <drebbe@intrepidcs.com>
This commit is contained in:
David Rebbe
2025-02-04 13:48:43 -05:00
parent c5ba2d8d32
commit 6dd4456f9a
81 changed files with 6731 additions and 364 deletions
+5
View File
@@ -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()
+2
View File
@@ -0,0 +1,2 @@
add_executable(libicsneo-simple-example src/main.c)
target_link_libraries(libicsneo-simple-example icsneoc2)
+454
View File
@@ -0,0 +1,454 @@
#include <icsneo/icsneoc2.h>
#include <stdio.h>
#include <time.h>
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#else
#include <unistd.h>
#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, &timestamp_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;
}
+2 -2
View File
@@ -259,8 +259,8 @@ void example4(const std::shared_ptr<icsneo::Device>& rada2b) {
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::MessageCallback>(
[] (std::shared_ptr<icsneo::Message> newMsg) {
if(newMsg->type == icsneo::Message::Type::Frame) {
const auto& frame = std::dynamic_pointer_cast<icsneo::Frame>(newMsg);
if(newMsg->type == icsneo::Message::Type::BusMessage) {
const auto& frame = std::dynamic_pointer_cast<icsneo::BusMessage>(newMsg);
if(frame && frame->network.getNetID() == icsneo::Network::NetID::I2C2) {
const auto& i2cMessage = std::dynamic_pointer_cast<icsneo::I2CMessage>(frame);
@@ -184,9 +184,9 @@ std::shared_ptr<icsneo::Device> selectDevice(const std::vector<std::shared_ptr<i
void printMessage(const std::shared_ptr<icsneo::Message>& 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<icsneo::Frame>(message);
auto frame = std::static_pointer_cast<icsneo::BusMessage>(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<icsneo::Message>& 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<icsneo::CANErrorMessage>(message);
+2 -2
View File
@@ -96,8 +96,8 @@ int main() {
std::cout << "OK" << std::endl << std::endl;
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
if(icsneo::Message::Type::Frame == message->type) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(icsneo::Message::Type::BusMessage == message->type) {
auto frame = std::static_pointer_cast<icsneo::BusMessage>(message);
if(icsneo::Network::Type::LIN == frame->network.getType()) {
auto msg = std::static_pointer_cast<icsneo::LINMessage>(message);
std::cout << msg->network << " RX frame | ID: 0x" << std::hex << static_cast<int>(msg->ID) << " | ";
+2 -2
View File
@@ -92,8 +92,8 @@ int main()
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message)
{
if(icsneo::Message::Type::Frame == message->type) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(icsneo::Message::Type::BusMessage == message->type) {
auto frame = std::static_pointer_cast<icsneo::BusMessage>(message);
if(icsneo::Network::Type::MDIO == frame->network.getType()) {
auto msg = std::static_pointer_cast<icsneo::MDIOMessage>(message);
std::cout << msg->network << " " << ((msg->isTXMsg)? "TX" : "RX") << " frame\n";
+5 -5
View File
@@ -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<icsneo::MessageCallback>([](std::shared_ptr<icsneo::Message> 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<icsneo::Frame>(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<icsneo::BusMessage>(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<icsneo::EthernetMessage>(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<icsneo::CANErrorMessage>(message);
+7 -7
View File
@@ -16,13 +16,13 @@ void onEvent(std::shared_ptr<icsneo::APIEvent> event) {
std::cout << event->describe() << std::endl;
}
std::vector<std::shared_ptr<icsneo::Frame>> constructRandomFrames(size_t frameCount, MessageType frameType) {
std::vector<std::shared_ptr<icsneo::BusMessage>> 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<std::shared_ptr<icsneo::Frame>> frames;
std::vector<std::shared_ptr<icsneo::BusMessage>> 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<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> msg) {
if(msg->type != icsneo::Message::Type::Frame) {
if(msg->type != icsneo::Message::Type::BusMessage) {
return;
}
const auto frame = std::static_pointer_cast<icsneo::Frame>(msg);
const auto frame = std::static_pointer_cast<icsneo::BusMessage>(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<std::shared_ptr<icsneo::Frame>> frames;
std::vector<std::shared_ptr<icsneo::BusMessage>> 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<MessageType>(i));
@@ -216,10 +216,10 @@ int main(int argc, char* argv[]) {
size_t currentMessage = 0;
rxDevice->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> msg) {
if(msg->type != icsneo::Message::Type::Frame) {
if(msg->type != icsneo::Message::Type::BusMessage) {
return;
}
auto frame = std::static_pointer_cast<icsneo::Frame>(msg);
auto frame = std::static_pointer_cast<icsneo::BusMessage>(msg);
if(frames[currentMessage]->data == frame->data) {
currentMessage++;
}
+19
View File
@@ -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`
+3
View File
@@ -0,0 +1,3 @@
module simple
go 1.23.4
+354
View File
@@ -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, &timestampResolution); 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
}
+22
View File
@@ -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/
+72
View File
@@ -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);
}
+72
View File
@@ -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 <url>`, 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 <url>` 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",
},
}
+421
View File
@@ -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, &timestamp_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;
}