From 3ddb83270887c3def78dc3f7d969480923fef768 Mon Sep 17 00:00:00 2001 From: Yasser Yassine Date: Wed, 12 Apr 2023 16:29:48 +0000 Subject: [PATCH] A2B: Add additional examples Device: Add coremini loading example --- .gitignore | 1 + .../callback/streamoutput/a2bdecoder.cpp | 2 +- examples/CMakeLists.txt | 7 ++ examples/cpp/a2b/README.md | 48 ---------- examples/cpp/a2b/src/a2b.cpp | 89 +++++++++++++++++-- examples/cpp/coremini/CMakeLists.txt | 27 ++++++ examples/cpp/coremini/src/coremini.cpp | 68 ++++++++++++++ .../device/tree/rada2b/rada2bsettings.h | 8 +- 8 files changed, 188 insertions(+), 62 deletions(-) delete mode 100644 examples/cpp/a2b/README.md create mode 100644 examples/cpp/coremini/CMakeLists.txt create mode 100644 examples/cpp/coremini/src/coremini.cpp diff --git a/.gitignore b/.gitignore index 7930b34..e907672 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ third-party/concurrentqueue/tests *.bak .vs .cache +*.wav \ No newline at end of file diff --git a/communication/message/callback/streamoutput/a2bdecoder.cpp b/communication/message/callback/streamoutput/a2bdecoder.cpp index 38bb843..e92cd4a 100644 --- a/communication/message/callback/streamoutput/a2bdecoder.cpp +++ b/communication/message/callback/streamoutput/a2bdecoder.cpp @@ -69,7 +69,7 @@ A2BDecoder::A2BDecoder( ) : A2BDecoder(std::make_unique(filename, std::ios::binary), chSize16, chMap) { } A2BDecoder::operator bool() const { - return initialized && *stream; + return initialized && stream->good() && !stream->eof(); } void A2BDecoder::initializeFromHeader() { diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f8d21a8..de44741 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,6 +5,8 @@ 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) option(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE "Build the LIN example." ON) +option(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE "Build the Coremini example." ON) + # Disabled until we properly build these in-tree # option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF) # option(LIBICSNEO_BUILD_JAVA_INTERACTIVE_EXAMPLE "Build the command-line interactive Java example." OFF) @@ -36,6 +38,11 @@ endif() if(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE) add_subdirectory(cpp/lin) endif() + +if(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE) + add_subdirectory(cpp/coremini) +endif() + # if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE) # add_subdirectory(csharp) # endif() diff --git a/examples/cpp/a2b/README.md b/examples/cpp/a2b/README.md deleted file mode 100644 index 2003d4d..0000000 --- a/examples/cpp/a2b/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# 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™ diff --git a/examples/cpp/a2b/src/a2b.cpp b/examples/cpp/a2b/src/a2b.cpp index 362bf9a..f022777 100644 --- a/examples/cpp/a2b/src/a2b.cpp +++ b/examples/cpp/a2b/src/a2b.cpp @@ -12,6 +12,7 @@ #include #include #include +#include static constexpr size_t numFramesInWave = 48; @@ -70,7 +71,7 @@ void example0(std::shared_ptr& rada2b) { decoder.outputAll(rada2b); // Output entire wave file - elapsedTime += ((double)numFramesInWave) * 1.0/48000.0; + elapsedTime += (static_cast(numFramesInWave)) * 1.0/48000.0; decoder.stream->clear(); decoder.stream->seekg(0, std::ios::beg); @@ -78,7 +79,7 @@ void example0(std::shared_ptr& rada2b) { while(decoder && elapsedTime < 5.0) { auto msg = decoder.decode(); rada2b->transmit(msg); - elapsedTime += ((double)msg->getNumFrames())*1.0/48000.0; + elapsedTime += (static_cast(msg->getNumFrames()))*1.0/48000.0; } decoder.stream->clear(); @@ -105,11 +106,11 @@ void example1(std::shared_ptr& rada2b) { // Example 2: RADA2B settings void example2(std::shared_ptr& rada2b) { - uint32_t tdm; + uint8_t numChannels; { // Get device settings auto* settings = rada2b->settings.get(); - auto* rada2bSettings = (icsneo::RADA2BSettings*)settings; + auto* rada2bSettings = static_cast(settings); // Check if monitor mode is enabled auto type = rada2bSettings->getNodeType(icsneo::RADA2BSettings::RADA2BDevice::Monitor); @@ -121,9 +122,9 @@ void example2(std::shared_ptr& rada2b) { } // Get current tdm mode - tdm = rada2bSettings->getTDMModeInt(icsneo::RADA2BSettings::RADA2BDevice::Node); + numChannels = rada2bSettings->getNumChannels(icsneo::RADA2BSettings::RADA2BDevice::Node); - std::cout << "Current tdm mode: " << tdm << std::endl; + std::cout << "Current num channels: " << static_cast(numChannels) << std::endl; // Set node type to master node. rada2bSettings->setNodeType(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::NodeType::Master); @@ -174,6 +175,68 @@ void example3() { std::cout << "Was received from monitor: " << msg.isMonitorMsg() << std::endl; } +// Example 4: Packaging and transmitting sine wave using A2BMessage API +void example4(std::shared_ptr& rada2b) { + std::cout << "Transmitting a 1000 hz sine wave." << std::endl; + + float deltaTime = static_cast(1.0/48000.0); + float elapsedTime = 0.0; + float twoPI = static_cast(2.0*atan(1.0)*4.0); + float frequency = 1000; + float amplitude = static_cast((1 << 23) - 1); + + uint8_t icsChannel = 0; // Play audio on channel 2, upstream, see docs for details + + + // Play for roughly 5 seconds + while(elapsedTime < 5.0) { + // Allocate message + std::shared_ptr a2bmsgPtr = std::make_shared(static_cast(4), false, static_cast(2048)); + + icsneo::A2BMessage& a2bmsg = *a2bmsgPtr.get(); + a2bmsg.network = icsneo::Network(icsneo::Network::NetID::A2B2); + + for(size_t frame = 0; frame < a2bmsg.getNumFrames(); frame++) { + + // Sine wave sample, amplitude 1000, frequency 1000 hz + + float contSample = amplitude*sin(twoPI*frequency*elapsedTime); + icsneo::A2BPCMSample sample = static_cast(contSample); + + // Set sample for each frame in message + a2bmsg[frame][icsChannel] = sample; + + elapsedTime+=deltaTime; + } + + // Transmit message to device + + if(!rada2b->transmit(a2bmsgPtr)) { + std::cout << "Failed to transmit." << std::endl; + } + } + + +} + + +// Example 5: Wave loop back +void example5(std::shared_ptr& rada2b) { + + auto listener = [&rada2b]() { + auto handler = rada2b->addMessageCallback(std::make_shared("looped.wav", 48000)); + std::this_thread::sleep_for(std::chrono::seconds(5)); + rada2b->removeMessageCallback(handler); + }; + + // Listen on another thread + std::thread listenerThread{listener}; + + // Transmit wave file using example0 + + example0(rada2b); + listenerThread.join(); +} void displayUsage() { std::cout << "libicsneo A2B example" << std::endl; @@ -187,11 +250,13 @@ void displayUsage() { std::cout << "Example options:" << std::endl; std::cout << "0\ttx" << std::endl; std::cout << "1\trx" << std::endl; - std::cout << "2\trx split channel" << std::endl; + std::cout << "2\tSet RADA2B settings" << std::endl; std::cout << "3\tA2BMessage API" << std::endl; + std::cout << "4\tPackaging and transmitting sine wave using A2BMessage API" << std::endl; + std::cout << "5\tWave loopback" << std::endl; } -int main(int argc, char **argv) { +int main(int argc, char** argv) { std::vector arguments(argv, argv + argc); if(argc > 4 || argc == 1) { std::cerr << "Invalid usage." << std::endl; @@ -212,7 +277,7 @@ int main(int argc, char **argv) { int option = atoi(arguments[2].c_str()); - if(option < 0 || option > 3) { + if(option < 0 || option > 5) { std::cerr << "Invalid usage." << std::endl; displayUsage(); return EXIT_FAILURE; @@ -274,6 +339,12 @@ int main(int argc, char **argv) { case 3: example3(); break; + case 4: + example4(rada2b); + break; + case 5: + example5(rada2b); + break; default: break; } diff --git a/examples/cpp/coremini/CMakeLists.txt b/examples/cpp/coremini/CMakeLists.txt new file mode 100644 index 0000000..aeabd27 --- /dev/null +++ b/examples/cpp/coremini/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.2) +project(libicsneocpp-coremini VERSION 0.2.0) + +set(CMAKE_CXX_STANDARD_REQUIRED 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-coremini src/coremini.cpp) +target_link_libraries(libicsneocpp-coremini icsneocpp) \ No newline at end of file diff --git a/examples/cpp/coremini/src/coremini.cpp b/examples/cpp/coremini/src/coremini.cpp new file mode 100644 index 0000000..08f5b69 --- /dev/null +++ b/examples/cpp/coremini/src/coremini.cpp @@ -0,0 +1,68 @@ +// Usage: +// ./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH] + + +#include +#include +#include + +void displayUsage() { + std::cout << "Usage:\n"; + std::cout << "./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH]\n"; +} + + +int main(int argc, char** argv) { + std::vector arguments(argv, argv + argc); + + if(arguments.size() != 3) { + displayUsage(); + return EXIT_FAILURE; + } + + + std::cout << icsneo::GetVersion() << std::endl; + + const auto& devices = icsneo::FindAllDevices(); + + auto it = std::find_if( + devices.begin(), + devices.end(), + [&arguments](const auto &d) + { return d->getSerial() == arguments[1]; }); + + if(it == devices.end()) { + std::cout << "Failed to find device." << std::endl; + return EXIT_FAILURE; + } + + + std::shared_ptr device = *it; + if(!device->open()) { + std::cout << "Failed to open device." << std::endl; + std::cout << icsneo::GetLastError() << std::endl; + return EXIT_FAILURE; + } + + if(!device->goOnline()) { + std::cout << "Failed to go online." << std::endl; + std::cout << icsneo::GetLastError() << std::endl; + return EXIT_FAILURE; + } + + if (!device->uploadCoremini(std::make_unique(arguments[2], std::ios::binary), icsneo::Disk::MemoryType::Flash)) + { + std::cout << "Failed to upload coremini" << std::endl; + std::cout << icsneo::GetLastError() << std::endl; + } + + if (!device->startScript(icsneo::Disk::MemoryType::Flash)) + { + std::cout << "Failed to start script" << std::endl; + std::cout << icsneo::GetLastError() << std::endl; + } + + device->goOffline(); + device->close(); + return 0; +} \ No newline at end of file diff --git a/include/icsneo/device/tree/rada2b/rada2bsettings.h b/include/icsneo/device/tree/rada2b/rada2bsettings.h index bbc7716..10db525 100644 --- a/include/icsneo/device/tree/rada2b/rada2bsettings.h +++ b/include/icsneo/device/tree/rada2b/rada2bsettings.h @@ -125,10 +125,10 @@ public: auto cfg = getStructurePointer(); auto &deviceSettings = device == RADA2BDevice::Monitor ? cfg->a2b_monitor : cfg->a2b_node; - return (TDMMode)(deviceSettings.tdmMode); + return static_cast(deviceSettings.tdmMode); } - uint8_t getTDMModeInt(RADA2BDevice device) const { + uint8_t getNumChannels(RADA2BDevice device) const { return tdmModeToChannelNum(getTDMMode(device)); } @@ -136,7 +136,7 @@ public: auto cfg = getStructurePointer(); auto &deviceSettings = device == RADA2BDevice::Monitor ? cfg->a2b_monitor : cfg->a2b_node; - return (ChannelSize)(~(deviceSettings.flags & a2bSettingsFlag16bit)); + return static_cast(~(deviceSettings.flags & a2bSettingsFlag16bit)); } uint8_t getChannelOffset(RADA2BDevice device, A2BMessage::A2BDirection dir) const { @@ -154,7 +154,7 @@ public: auto cfg = getStructurePointer(); auto &deviceSettings = device == RADA2BDevice::Monitor ? cfg->a2b_monitor : cfg->a2b_node; - return (NodeType)(deviceSettings.nodeType); + return static_cast(deviceSettings.nodeType); } void setNodeType(RADA2BDevice device, NodeType newType) {