Move examples into tree

See history at https://github.com/intrepidcs/libicsneo-examples/tree/v0.2.0-dev
This commit is contained in:
Paul Hollinsky
2020-08-06 15:41:48 -04:00
parent 2079037ae4
commit f49f65c3ed
54 changed files with 20217 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-interactive-example VERSION 0.2.0)
set(CMAKE_CXX_STANDARD 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-interactive-example src/InteractiveExample.cpp)
target_link_libraries(libicsneocpp-interactive-example icsneocpp)
+48
View File
@@ -0,0 +1,48 @@
# libicsneo C++ Example
This is an example console application which uses libicsneo to connect to an Intrepid Control Systems hardware device. It has both interactive and simple examples for sending and receiving CAN & CAN FD traffic.
## Building
This example shows how to use the C++ version of libicsneo with CMake. It will build libicsneo along with your project.
First, you need to clone the repository onto your local machine. Run:
```shell
git clone https://github.com/intrepidcs/libicsneo-examples --recursive
```
Alternatively, if you cloned without the `--recursive flag`, you must enter the `libicsneo-examples` folder and run the following:
```shell
git submodule update --recursive --init
```
If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build!
### Windows using Visual Studio 2017+
1. Launch Visual Studio and open the `libicsneo-examples` folder.
2. Choose `File->Open->CMake...`
3. Navigate to the `libicsneocpp-example` folder and select the `CMakeLists.txt` there.
4. Visual Studio will process the CMake project.
5. Choose the dropdown attached to the green play button (labelled "select startup item...") in the toolbar.
6. Select `libicsneocpp-interactive-example.exe` or `libicsneocpp-simple-example.exe`
7. Press the green play button to compile and run the example.
### Ubuntu 18.04 LTS
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make libicsneocpp-interactive-example` to build.
* Hint! Speed up your build by using multiple processors! Use `make libicsneocpp-interactive-example -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Now run `sudo ./libicsneocpp-interactive-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
7. If you wish to run the simple example instead, replace any instances of "interactive" with "simple" in steps 5 and 6.
### macOS
Instructions coming soon™
@@ -0,0 +1,618 @@
#include <iostream>
#include <string>
#include <ctype.h>
#include <vector>
#include <map>
// Include icsneo/icsneocpp.h to access library functions
#include "icsneo/icsneocpp.h"
/**
* \brief Prints all current known devices to output in the following format:
* [num] DeviceType SerialNum Connected: Yes/No Online: Yes/No Msg Polling: On/Off
*
* If any devices could not be described due to an error, they will appear in the following format:
* Description for device num not available!
*/
void printAllDevices(const std::vector<std::shared_ptr<icsneo::Device>>& devices) {
if(devices.size() == 0) {
std::cout << "No devices found! Please scan for new devices." << std::endl;
}
int index = 1;
for(auto device : devices) {
std::cout << "[" << index << "] " << device->describe() << "\tConnected: " << (device->isOpen() ? "Yes\t" : "No\t");
std::cout << "Online: " << (device->isOnline() ? "Yes\t" : "No\t");
std::cout << "Msg Polling: " << (device->isMessagePollingEnabled() ? "On" : "Off") << std::endl;
index++;
}
}
// Prints the main menu options to output
void printMainMenu() {
std::cout << "Press the letter next to the function you want to use:" << std::endl;
std::cout << "A - List all devices" << std::endl;
std::cout << "B - Find all devices" << std::endl;
std::cout << "C - Open/close" << std::endl;
std::cout << "D - Go online/offline" << std::endl;
std::cout << "E - Enable/disable message polling" << std::endl;
std::cout << "F - Get messages" << std::endl;
std::cout << "G - Send messages" << std::endl;
std::cout << "H - Get events" << std::endl;
std::cout << "I - Set HS CAN to 250K" << std::endl;
std::cout << "J - Set LSFT CAN to 250K" << std::endl;
std::cout << "K - Add/Remove a message callback" << std::endl;
std::cout << "X - Exit" << std::endl;
}
/**
* \brief Gets all current API events (info and warning level) and prints them to output
* Flushes all current API events, meaning future calls (barring any new events) will not detect any further API events
*/
void printAPIEvents() {
// Match all events
auto events = icsneo::GetEvents(icsneo::EventFilter());
if(events.size() == 1) {
std::cout << "1 API event found!" << std::endl;
} else {
std::cout << events.size() << " API events found!" << std::endl;
}
for(auto event : events) {
std::cout << event << std::endl;
}
}
/**
* \brief Gets all current API warnings and prints them to output
* Flushes all current API warnings, meaning future calls (barring any new warnings) will not detect any further API warnings
*/
void printAPIWarnings() {
// Match all warning events, regardless of device
auto warnings = icsneo::GetEvents(icsneo::EventFilter(nullptr, icsneo::APIEvent::Severity::EventWarning));
if(warnings.size() == 1) {
std::cout << "1 API warning found!" << std::endl;
} else {
std::cout << warnings.size() << " API warnings found!" << std::endl;
}
for(auto warning : warnings) {
std::cout << warning << std::endl;
}
}
/**
* \brief Gets all current device events and prints them to output.
* Flushes all current device events, meaning future calls (barring any new events) will not detect any further device events for this device
*/
void printDeviceEvents(std::shared_ptr<icsneo::Device> device) {
// Match all events for the specified device
auto events = icsneo::GetEvents(icsneo::EventFilter(device.get()));
if(events.size() == 1) {
std::cout << "1 device event found!" << std::endl;
} else {
std::cout << events.size() << " device events found!" << std::endl;
}
for(auto event : events) {
std::cout << event << std::endl;
}
}
/**
* \brief Gets all current device warnings and prints them to output.
* Flushes all current device warnings, meaning future calls (barring any new warnings) will not detect any further device warnings for this device
*/
void printDeviceWarnings(std::shared_ptr<icsneo::Device> device) {
// Match all warning events for the specified device
auto events = icsneo::GetEvents(icsneo::EventFilter(device.get(), icsneo::APIEvent::Severity::EventWarning));
if(events.size() == 1) {
std::cout << "1 device warning found!" << std::endl;
} else {
std::cout << events.size() << " device warnings found!" << std::endl;
}
for(auto event : events) {
std::cout << event << std::endl;
}
}
/**
* \brief Used to check character inputs for correctness (if they are found in an expected list)
* \param[in] numArgs the number of possible options for the expected character
* \param[in] ... the possible options for the expected character
* \returns the entered character
*
* This function repeatedly prompts the user for input until a matching input is entered
* Example usage:
* char input = getCharInput(std::vector<char> {'F', 'u', 'b', 'a', 'r'});
*/
char getCharInput(std::vector<char> allowed) {
bool found = false;
std::string input;
while(!found) {
std::cin >> input;
if(input.length() == 1) {
for(char compare : allowed) {
if(compare == input.c_str()[0]) {
found = true;
break;
}
}
}
if(!found) {
std::cout << "Input did not match expected options. Please try again." << std::endl;
}
}
return input.c_str()[0];
}
/**
* \brief Prompts the user to select a device from the list of currently known devices
* \returns a pointer to the device in devices[] selected by the user
* Requires an input from 1-9, so a maximum of 9 devices are supported
*/
std::shared_ptr<icsneo::Device> selectDevice(const std::vector<std::shared_ptr<icsneo::Device>>& from) {
printf("Please select a device:\n");
printAllDevices(from);
printf("\n");
int selectedDeviceNum = 10;
while((size_t) selectedDeviceNum > from.size()) {
char deviceSelection = getCharInput(std::vector<char> {'1', '2', '3', '4', '5', '6', '7', '8', '9'});
selectedDeviceNum = deviceSelection - '0';
if((size_t) selectedDeviceNum > from.size()) {
std::cout << "Selected device out of range!" << std::endl;
}
}
std::cout << std::endl;
return from.at(selectedDeviceNum - 1);
}
int main() {
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl << std::endl;
size_t msgLimit = 50000;
std::vector<std::shared_ptr<icsneo::Device>> devices;
std::map<std::shared_ptr<icsneo::Device>, std::vector<int>> callbacks;
std::shared_ptr<icsneo::Device> selectedDevice;
while(true) {
printMainMenu();
std::cout << std::endl;
char input = getCharInput(std::vector<char> {'A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'H', 'h', 'I', 'i', 'J', 'j', 'K', 'k', 'X', 'x'});
std::cout << std::endl;
switch(input) {
// List current devices
case 'A':
case 'a':
printAllDevices(devices);
std::cout << std::endl;
break;
// Find all devices
case 'B':
case 'b':
{
devices = icsneo::FindAllDevices();
for(auto device : devices) {
callbacks.insert({device, std::vector<int>()});
}
if(devices.size() == 1) {
std::cout << "1 device found!" << std::endl;
} else {
std::cout << devices.size() << " devices found!" << std::endl;
}
printAllDevices(devices);
std::cout << std::endl;
break;
}
// Open/Close
case 'C':
case 'c':
{
// Select a device and get its description
if(devices.size() == 0) {
std::cout << "No devices found! Please scan for new devices." << std::endl << std::endl;
break;
}
selectedDevice = selectDevice(devices);
std::cout << "Would you like to open or close " << selectedDevice->describe() << "?" << std::endl;
std::cout << "[1] Open" << std::endl << "[2] Close" << std::endl << "[3] Cancel" << std::endl << std::endl;
char selection = getCharInput(std::vector<char> {'1', '2', '3'});
std::cout << std::endl;
switch(selection) {
case '1':
if(selectedDevice->open()) {
std::cout << selectedDevice->describe() << " successfully opened!" << std::endl << std::endl;
} else {
std::cout << selectedDevice->describe() << " failed to open!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
std::cout << std::endl;
}
break;
case '2':
// Attempt to close the device
if(selectedDevice->close()) {
std::cout << "Successfully closed " << selectedDevice->describe() << "!" << std::endl << std::endl;
selectedDevice = NULL;
} else {
std::cout << "Failed to close " << selectedDevice->describe() << "!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
std::cout << std::endl;
}
break;
default:
std::cout << "Canceling!" << std::endl << std::endl;
break;
}
}
break;
// Go online/offline
case 'D':
case 'd':
{
// Select a device and get its description
if(devices.size() == 0) {
std::cout << "No devices found! Please scan for new devices." << std::endl << std::endl;
break;
}
selectedDevice = selectDevice(devices);
std::cout << "Would you like to have " << selectedDevice->describe() << " go online or offline?" << std::endl;
std::cout << "[1] Online" << std::endl << "[2] Offline" << std::endl << "[3] Cancel" << std::endl << std::endl;
char selection = getCharInput(std::vector<char> {'1', '2', '3'});
std::cout << std::endl;
switch(selection) {
case '1':
// Attempt to have the selected device go online
if(selectedDevice->goOnline()) {
std::cout << selectedDevice->describe() << " successfully went online!" << std::endl << std::endl;
} else {
std::cout << selectedDevice->describe() << " failed to go online!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
std::cout << std::endl;
}
break;
case '2':
// Attempt to go offline
if(selectedDevice->goOffline()) {
std::cout << selectedDevice->describe() << " successfully went offline!" << std::endl << std::endl;
} else {
std::cout << selectedDevice->describe() << " failed to go offline!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
std::cout << std::endl;
}
break;
default:
std::cout << "Canceling!" << std::endl << std::endl;
break;
}
}
break;
// Enable/disable message polling
case 'E':
case 'e':
{
// Select a device and get its description
if(devices.size() == 0) {
std::cout << "No devices found! Please scan for new devices." << std::endl << std::endl;
break;
}
selectedDevice = selectDevice(devices);
std::cout << "Would you like to enable or disable message polling for " << selectedDevice->describe() << "?" << std::endl;
std::cout << "[1] Enable" << std::endl << "[2] Disable" << std::endl << "[3] Cancel" << std::endl << std::endl;
char selection = getCharInput(std::vector<char> {'1', '2', '3'});
std::cout << std::endl;
switch(selection) {
case '1':
// Attempt to enable message polling
if(selectedDevice->enableMessagePolling()) {
std::cout << "Successfully enabled message polling for " << selectedDevice->describe() << "!" << std::endl << std::endl;
} else {
std::cout << "Failed to enable message polling for " << selectedDevice->describe() << "!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
std::cout << std::endl;
}
// Manually setting the polling message limit as done below is optional
// It will default to 20k if not set
selectedDevice->setPollingMessageLimit(50000);
if(selectedDevice->getPollingMessageLimit() == 50000) {
std::cout << "Successfully set polling message limit for " << selectedDevice->describe() << "!" << std::endl << std::endl;
} else {
std::cout << "Failed to set polling message limit for " << selectedDevice->describe() << "!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
std::cout << std::endl;
}
break;
case '2':
// Attempt to disable message polling
if(selectedDevice->disableMessagePolling()) {
std::cout << "Successfully disabled message polling for " << selectedDevice->describe() << "!" << std::endl;
} else {
std::cout << "Failed to disable message polling for " << selectedDevice->describe() << "!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
std::cout << std::endl;
}
break;
default:
std::cout << "Canceling!" << std::endl << std::endl;
break;
}
}
break;
// Get messages
case 'F':
case 'f':
{
// Select a device and get its description
if(devices.size() == 0) {
std::cout << "No devices found! Please scan for new devices." << std::endl << std::endl;
break;
}
selectedDevice = selectDevice(devices);
std::vector<std::shared_ptr<icsneo::Message>> msgs;
// Attempt to get messages, limiting the number of messages at once to 50,000
// A third parameter of type std::chrono::milliseconds is also accepted if a timeout is desired
if(!selectedDevice->getMessages(msgs, msgLimit)) {
std::cout << "Failed to get messages for " << selectedDevice->describe() << "!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
std::cout << std::endl;
break;
}
if(msgs.size() == 1) {
std::cout << "1 message received from " << selectedDevice->describe() << "!" << std::endl;
} else {
std::cout << msgs.size() << " messages received from " << selectedDevice->describe() << "!" << std::endl;
}
// Print out the received messages
for(auto msg : msgs) {
switch(msg->network.getType()) {
case icsneo::Network::Type::CAN:
{
// A message of type CAN is guaranteed to be a CANMessage, so we can static cast safely
auto canMsg = std::static_pointer_cast<icsneo::CANMessage>(msg);
std::cout << "\t0x" << std::setfill('0') << std::setw(3) << std::hex << (int) canMsg->arbid << " [" << canMsg->data.size() << "] " << std::dec;
for(auto data : canMsg->data) {
std::cout << std::setfill('0') << std::setw(2) << std::hex << (int) data << " " << std::dec;
}
std::cout << canMsg->timestamp << std::endl;
break;
}
default:
if(msg->network.getNetID() != icsneo::Network::NetID::Device) {
std::cout << "\tMessage on netid " << msg->network.GetNetIDString(msg->network.getNetID()) << " with length " << msg->data.size() << std::endl;
}
break;
}
}
std::cout << std::endl;
}
break;
// Send messages
case 'G':
case 'g':
{
// Select a device and get its description
if(devices.size() == 0) {
std::cout << "No devices found! Please scan for new devices." << std::endl << std::endl;
break;
}
selectedDevice = selectDevice(devices);
std::cout << "Transmitting a normal CAN frame..." << std::endl;
auto msg = std::make_shared<icsneo::CANMessage>();
msg->network = icsneo::Network::NetID::HSCAN;
msg->arbid = 0x120;
msg->data.insert(msg->data.end(), {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff});
msg->isExtended = false;
msg->isCANFD = false;
// Attempt to transmit the sample msg
if(selectedDevice->transmit(msg)) {
std::cout << "Message transmit successful!" << std::endl;
} else {
std::cout << "Failed to transmit message to " << selectedDevice->describe() << "!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
}
/** Example of CAN FD
std::cout << "Transmitting an extended CAN FD frame... " << std::endl;
auto txMessage = std::make_shared<icsneo::CANMessage>();
txMessage->network = icsneo::Network::NetID::HSCAN;
txMessage->arbid = 0x1C5001C5;
txMessage->data.insert(txMessage->data.end(), {0xaa, 0xbb, 0xcc});
// The DLC will come from the length of the data vector
txMessage->isExtended = true;
txMessage->isCANFD = true;
// Attempt to transmit the sample msg
if(selectedDevice->transmit(txMessage)) {
std::cout << "Extended CAN FD frame transmit successful!" << std::endl;
} else {
std::cout << "Failed to transmit extended CAN FD frame to " << selectedDevice->describe() << "!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
}
*/
std::cout << std::endl;
}
break;
// Get events
case 'H':
case 'h':
{
// Prints all events
printAPIEvents();
std::cout << std::endl;
}
break;
// Set HS CAN to 250k
case 'I':
case 'i':
{
// Select a device and get its description
if(devices.size() == 0) {
std::cout << "No devices found! Please scan for new devices." << std::endl << std::endl;
break;
}
selectedDevice = selectDevice(devices);
// Attempt to set baudrate and apply settings
if(selectedDevice->settings->setBaudrateFor(icsneo::Network::NetID::HSCAN, 250000) && selectedDevice->settings->apply()) {
std::cout << "Successfully set HS CAN baudrate for " << selectedDevice->describe() << " to 250k!" << std::endl;
} else {
std::cout << "Failed to set HS CAN baudrate for " << selectedDevice->describe() << " to 250k!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
}
std::cout << std::endl;
}
break;
// Set LSFT CAN to 250k
case 'J':
case 'j':
{
// Select a device and get its description
if(devices.size() == 0) {
std::cout << "No devices found! Please scan for new devices." << std::endl << std::endl;
break;
}
selectedDevice = selectDevice(devices);
// Attempt to set baudrate and apply settings
if(selectedDevice->settings->setBaudrateFor(icsneo::Network::NetID::LSFTCAN, 250000) && selectedDevice->settings->apply()) {
std::cout << "Successfully set LSFT CAN baudrate for " << selectedDevice->describe() << " to 250k!" << std::endl;
} else {
std::cout << "Failed to set LSFT CAN baudrate for " << selectedDevice->describe() << " to 250k!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
}
std::cout << std::endl;
}
break;
// Add/Remove a message callback
case 'K':
case 'k':
{
// Select a device and get its description
if(devices.size() == 0) {
std::cout << "No devices found! Please scan for new devices." << std::endl << std::endl;
break;
}
selectedDevice = selectDevice(devices);
std::cout << "Would you like to add or remove a message callback for " << selectedDevice->describe() << "?" << std::endl;
std::cout << "[1] Add" << std::endl << "[2] Remove" << std::endl << "[3] Cancel" << std::endl << std::endl;
char selection = getCharInput(std::vector<char> {'1', '2', '3'});
std::cout << std::endl;
switch(selection) {
case '1':
{
// Shameless copy-paste from get messages above, demonstrating a callback
int callbackID = selectedDevice->addMessageCallback(icsneo::MessageCallback([](std::shared_ptr<icsneo::Message> msg){
switch(msg->network.getType()) {
case icsneo::Network::Type::CAN:
{
// A message of type CAN is guaranteed to be a CANMessage, so we can static cast safely
auto canMsg = std::static_pointer_cast<icsneo::CANMessage>(msg);
std::cout << "\t0x" << std::setfill('0') << std::setw(3) << std::hex << (int) canMsg->arbid << " [" << canMsg->data.size() << "] " << std::dec;
for(auto data : canMsg->data) {
std::cout << std::setfill('0') << std::setw(2) << std::hex << (int) data << " " << std::dec;
}
std::cout << canMsg->timestamp << std::endl;
break;
}
default:
if(msg->network.getNetID() != icsneo::Network::NetID::Device) {
std::cout << "\tMessage on netid " << msg->network.GetNetIDString(msg->network.getNetID()) << " with length " << msg->data.size() << std::endl;
}
break;
}
}));
if(callbackID != -1) {
std::cout << "Successfully added message callback to " << selectedDevice->describe() << "!" << std::endl;
callbacks.find(selectedDevice)->second.push_back(callbackID);
} else {
std::cout << "Failed to add message callback to " << selectedDevice->describe() << "!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
}
}
break;
case '2':
{
if(callbacks.find(selectedDevice)->second.size() == 0) {
std::cout << "No callbacks found for " << selectedDevice->describe() << "!" << std::endl;
break;
} else {
std::vector<char> allowed;
std::cout << "Which id would you like to remove?" << std::endl;
for(int id : callbacks.find(selectedDevice)->second) {
allowed.push_back(static_cast<char>(id) + '0');
std::cout << "[" << id << "]" << std::endl;
}
std::cout << std::endl;
int removeID = getCharInput(allowed) - '0';
std::cout << std::endl;
if(selectedDevice->removeMessageCallback(removeID)) {
std::cout << "Successfully removed callback id " << removeID << " from " << selectedDevice->describe() << "!" << std::endl;
} else {
std::cout << "Failed to remove message callback id " << removeID << " from " << selectedDevice->describe() << "!" << std::endl << std::endl;
std::cout << icsneo::GetLastError() << std::endl;;
}
}
}
break;
default:
std::cout << "Canceling!" << std::endl << std::endl;
break;
}
}
break;
// Exit
case 'X':
case 'x':
printf("Exiting program\n");
return 0;
default:
printf("Unexpected input, exiting!\n");
return 1;
}
}
return 0;
}
+27
View File
@@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-simple-example VERSION 0.2.0)
set(CMAKE_CXX_STANDARD 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-simple-example src/SimpleExample.cpp)
target_link_libraries(libicsneocpp-simple-example icsneocpp)
+48
View File
@@ -0,0 +1,48 @@
# libicsneo C++ Example
This is an example console application which uses libicsneo to connect to an Intrepid Control Systems hardware device. It has both interactive and simple examples for sending and receiving CAN & CAN FD traffic.
## Building
This example shows how to use the C++ version of libicsneo with CMake. It will build libicsneo along with your project.
First, you need to clone the repository onto your local machine. Run:
```shell
git clone https://github.com/intrepidcs/libicsneo-examples --recursive
```
Alternatively, if you cloned without the `--recursive flag`, you must enter the `libicsneo-examples` folder and run the following:
```shell
git submodule update --recursive --init
```
If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build!
### Windows using Visual Studio 2017+
1. Launch Visual Studio and open the `libicsneo-examples` folder.
2. Choose `File->Open->CMake...`
3. Navigate to the `libicsneocpp-example` folder and select the `CMakeLists.txt` there.
4. Visual Studio will process the CMake project.
5. Choose the dropdown attached to the green play button (labelled "select startup item...") in the toolbar.
6. Select `libicsneocpp-simple-example.exe`
7. Press the green play button to compile and run the example.
### Ubuntu 18.04 LTS
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make libicsneocpp-interactive-example` to build.
* Hint! Speed up your build by using multiple processors! Use `make libicsneocpp-interactive-example -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Now run `sudo ./libicsneocpp-interactive-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
7. If you wish to run the simple example instead, replace any instances of "interactive" with "simple" in steps 5 and 6.
### macOS
Instructions coming soon&trade;
+248
View File
@@ -0,0 +1,248 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
int main() {
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout<< "Supported devices:" << std::endl;
for(auto& dev : icsneo::GetSupportedDevices())
std::cout << '\t' << dev << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
// List off the devices
for(auto& device : devices)
std::cout << '\t' << device->getType() << " - " << device->getSerial() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for(auto& device : devices) {
std::cout << "Connecting to " << device->getType() << ' ' << device->getSerial() << "... ";
bool ret = device->open();
if(!ret) { // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl << std::endl;
continue;
}
std::cout << "OK" << std::endl;
std::cout << "\tGetting HSCAN Baudrate... ";
int64_t baud = device->settings->getBaudrateFor(icsneo::Network::NetID::HSCAN);
if(baud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (baud/1000) << "kbit/s" << std::endl;
std::cout << "\tSetting HSCAN to operate at 125kbit/s... ";
ret = device->settings->setBaudrateFor(icsneo::Network::NetID::HSCAN, 125000);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// Changes to the settings do not take affect until you call settings->apply()!
// When you get the baudrate here, you're reading what the device is currently operating on
std::cout << "\tGetting HSCAN Baudrate... (expected to be unchanged) ";
baud = device->settings->getBaudrateFor(icsneo::Network::NetID::HSCAN);
if(baud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (baud/1000) << "kbit/s" << std::endl;
std::cout << "\tGetting HSCANFD Baudrate... ";
baud = device->settings->getFDBaudrateFor(icsneo::Network::NetID::HSCAN);
if(baud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (baud/1000) << "kbit/s" << std::endl;
std::cout << "\tSetting HSCANFD to operate at 8Mbit/s... ";
ret = device->settings->setFDBaudrateFor(icsneo::Network::NetID::HSCAN, 8000000);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "\tGetting HSCANFD Baudrate... (expected to be unchanged) ";
baud = device->settings->getFDBaudrateFor(icsneo::Network::NetID::HSCAN);
if(baud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (baud/1000) << "kbit/s" << std::endl;
// Setting settings temporarily does not need to be done before committing to device EEPROM
// It's done here to test both functionalities
// Setting temporarily will keep these settings until another send/commit is called or a power cycle occurs
std::cout << "\tSetting settings temporarily... ";
ret = device->settings->apply(true);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// Now that we have applied, we expect that our operating baudrates have changed
std::cout << "\tGetting HSCAN Baudrate... ";
baud = device->settings->getBaudrateFor(icsneo::Network::NetID::HSCAN);
if(baud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (baud/1000) << "kbit/s" << std::endl;
std::cout << "\tGetting HSCANFD Baudrate... ";
baud = device->settings->getFDBaudrateFor(icsneo::Network::NetID::HSCAN);
if(baud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (baud/1000) << "kbit/s" << std::endl;
std::cout << "\tSetting settings permanently... ";
ret = device->settings->apply();
std::cout << (ret ? "OK\n\n" : "FAIL\n\n");
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
std::cout << "\tGoing online... ";
ret = device->goOnline();
if(!ret) {
std::cout << "FAIL" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
// A real application would just check the result of icsneo_goOnline() rather than calling this
// This function is intended to be called later on if needed
std::cout << "\tChecking online status... ";
ret = device->isOnline();
if(!ret) {
std::cout << "FAIL\n" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
// Now we can either register a handler (or multiple) for messages coming in
// or we can enable message polling, and then call device->getMessages periodically
// We're actually going to do both here, so first enable message polling
device->enableMessagePolling();
device->setPollingMessageLimit(100000); // Feel free to set a limit if you like, the default is a conservative 20k
// Keep in mind that 20k messages comes quickly at high bus loads!
// We can also register a handler
std::cout << "\tStreaming messages in for 3 seconds... " << std::endl;
// MessageCallbacks are powerful, and can filter on things like ArbID for you. See the documentation
auto handler = device->addMessageCallback(icsneo::MessageCallback([](std::shared_ptr<icsneo::Message> message) {
switch(message->network.getType()) {
case icsneo::Network::Type::CAN: {
// A message of type CAN is guaranteed to be a CANMessage, so we can static cast safely
auto canMessage = std::static_pointer_cast<icsneo::CANMessage>(message);
std::cout << "\t\tCAN ";
if(canMessage->isCANFD) {
std::cout << "FD ";
if(!canMessage->baudrateSwitch)
std::cout << "(No BRS) ";
}
// Print the Arbitration ID
std::cout << "0x" << std::hex << std::setw(canMessage->isExtended ? 8 : 3) << std::setfill('0') << canMessage->arbid;
// Print the DLC
std::cout << std::dec << " [" << canMessage->data.size() << "] ";
// Print the data
for(auto& databyte : canMessage->data)
std::cout << std::hex << std::setw(2) << (uint32_t)databyte << ' ';
// Print the timestamp
std::cout << std::dec << '(' << canMessage->timestamp << " ns since 1/1/2007)\n";
break;
}
case icsneo::Network::Type::Ethernet: {
auto ethMessage = std::static_pointer_cast<icsneo::EthernetMessage>(message);
std::cout << "\t\t" << ethMessage->network << " Frame - " << std::dec << ethMessage->data.size() << " bytes on wire\n";
std::cout << "\t\t Timestamped:\t"<< ethMessage->timestamp << " ns since 1/1/2007\n";
// The MACAddress may be printed directly or accessed with the `data` member
std::cout << "\t\t Source:\t" << ethMessage->getSourceMAC() << "\n";
std::cout << "\t\t Destination:\t" << ethMessage->getDestinationMAC();
// Print the data
for(size_t i = 0; i < ethMessage->data.size(); i++) {
if(i % 8 == 0)
std::cout << "\n\t\t " << std::hex << std::setw(4) << std::setfill('0') << i << '\t';
std::cout << std::hex << std::setw(2) << (uint32_t)ethMessage->data[i] << ' ';
}
std::cout << std::dec << std::endl;
break;
}
default:
// Ignoring non-network messages
break;
}
}));
std::this_thread::sleep_for(std::chrono::seconds(3));
device->removeMessageCallback(handler); // Removing the callback means it will not be called anymore
// Since we're using message polling, we can also get the messages which have come in for the past 3 seconds that way
// We could simply call getMessages and it would return a vector of message pointers to us
//auto messages = device->getMessages();
// For speed when calling repeatedly, we can also preallocate and continually reuse a vector
std::vector<std::shared_ptr<icsneo::Message>> messages;
messages.reserve(100000);
device->getMessages(messages);
std::cout << "\t\tGot " << messages.size() << " messages while polling" << std::endl;
// If we wanted to make sure it didn't grow and reallocate, we could also pass in a limit
// If there are more messages than the limit, we can call getMessages repeatedly
//device->getMessages(messages, 100);
// You are now the owner (or one of the owners, if multiple handlers are registered) of the shared_ptrs to the messages
// This means that when you let them go out of scope or reuse the vector, the messages will be freed automatically
// We can transmit messages
std::cout << "\tTransmitting an extended CAN FD frame... ";
auto txMessage = std::make_shared<icsneo::CANMessage>();
txMessage->network = icsneo::Network::NetID::HSCAN;
txMessage->arbid = 0x1C5001C5;
txMessage->data.insert(txMessage->data.end(), {0xaa, 0xbb, 0xcc});
// The DLC will come from the length of the data vector
txMessage->isExtended = true;
txMessage->isCANFD = true;
ret = device->transmit(txMessage); // This will return false if the device does not support CAN FD, or does not have HSCAN
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "\tTransmitting an ethernet frame on OP (BR) Ethernet 2... ";
auto ethTxMessage = std::make_shared<icsneo::EthernetMessage>();
ethTxMessage->network = icsneo::Network::NetID::OP_Ethernet2;
ethTxMessage->data.insert(ethTxMessage->data.end(), {
0x00, 0xFC, 0x70, 0x00, 0x01, 0x02, /* Destination MAC */
0x00, 0xFC, 0x70, 0x00, 0x01, 0x01, /* Source MAC */
0x00, 0x00, /* Ether Type */
0x01, 0xC5, 0x01, 0xC5 /* Payload (will automatically be padded on transmit unless you set `ethTxMessage->noPadding`) */
});
ret = device->transmit(ethTxMessage); // This will return false if the device does not support OP (BR) Ethernet 2
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
// Go offline, stop sending and receiving traffic
std::cout << "\tGoing offline... ";
ret = device->goOffline();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// Apply default settings
std::cout << "\tSetting default settings... ";
ret = device->settings->applyDefaults(); // This will also write to the device
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "\tDisconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
}
std::cout << "Press any key to continue..." << std::endl;
std::cin.get();
return 0;
}