mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-05 01:18:36 +02:00
Device: Add network mutex support
This commit is contained in:
committed by
Kyle Schwarz
parent
bf311ebe30
commit
c2f1022858
@@ -12,6 +12,9 @@ option(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE "Build the VSA example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE "Build the app error example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_FLEXRAY_EXAMPLE "Build the FlexRay example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_SPI_EXAMPLE "Build the SPI example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_MUTEX_EXAMPLE "Build the NetworkMutex example." ON)
|
||||
|
||||
add_compile_options(${LIBICSNEO_COMPILER_WARNINGS})
|
||||
|
||||
if(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE)
|
||||
add_subdirectory(c/interactive)
|
||||
@@ -67,4 +70,8 @@ endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_CPP_SPI_EXAMPLE)
|
||||
add_subdirectory(cpp/spi)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_CPP_MUTEX_EXAMPLE)
|
||||
add_subdirectory(cpp/mutex)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
add_executable(libicsneocpp-mutex-example src/MutexExample.cpp)
|
||||
target_link_libraries(libicsneocpp-mutex-example icsneocpp)
|
||||
@@ -0,0 +1,215 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <numeric>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4996) // STL time functions
|
||||
#endif
|
||||
|
||||
static std::vector<std::shared_ptr<icsneo::Device>> findDevices(icsneo::DeviceType type)
|
||||
{
|
||||
std::vector<std::shared_ptr<icsneo::Device>> ret;
|
||||
for(auto&& dev : icsneo::FindAllDevices()) {
|
||||
if(dev->getType() == type) {
|
||||
ret.push_back(dev);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::vector<icsneo::Network> supported_nets;
|
||||
static std::unordered_map<icsneo::Network::NetID, icsneo::NetworkMutexEvent> network_mutex_states;
|
||||
constexpr uint32_t MUTEX_PRIORITY = 12345678;
|
||||
constexpr uint32_t MUTEX_TTL_MS = 2500;
|
||||
|
||||
static void on_mutex_event(std::shared_ptr<icsneo::Message> msg) {
|
||||
if(msg->type == icsneo::Message::Type::NetworkMutex) {
|
||||
auto nmm = std::static_pointer_cast<icsneo::NetworkMutexMessage>(msg);
|
||||
if(nmm->event.has_value() && nmm->networks.size())
|
||||
{
|
||||
auto network = *nmm->networks.begin();
|
||||
if(std::find_if(supported_nets.begin(), supported_nets.end(), [network](const icsneo::Network& net){ return net.getNetID() == network; }) != supported_nets.end())
|
||||
{
|
||||
std::cout << icsneo::Network::GetNetIDString(static_cast<icsneo::Network::NetID>(network)) << ": Mutex " << icsneo::NetworkMutexMessage::GetNetworkMutexEventString(nmm->event.value()) << std::endl;
|
||||
network_mutex_states[network] = *nmm->event;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void do_mutexed_operation(std::shared_ptr<icsneo::Device> device, icsneo::Network::NetID network, std::function<void()>&& operation)
|
||||
{
|
||||
// Demonstrate taking a mutex on a single / group of networks
|
||||
auto lt = device->lockNetworks({network}, MUTEX_PRIORITY, MUTEX_TTL_MS, icsneo::NetworkMutexType::TxExclusive, on_mutex_event);
|
||||
if(lt.has_value()) {
|
||||
while(network_mutex_states.find(network) == network_mutex_states.end() ||
|
||||
network_mutex_states[network] != icsneo::NetworkMutexEvent::Acquired)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
operation();
|
||||
|
||||
device->unlockNetworks({network});
|
||||
while(network_mutex_states.find(network) != network_mutex_states.end() &&
|
||||
network_mutex_states[network] == icsneo::NetworkMutexEvent::Acquired)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
device->removeMessageCallback(*lt);
|
||||
} else {
|
||||
std::cout << "Failed to acquire mutex for " << icsneo::Network::GetNetIDString(network) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Print version
|
||||
std::cout << "libicsneo version " << icsneo::GetVersion() << std::endl;
|
||||
|
||||
std::cout << "\nFinding devices... " << std::flush;
|
||||
auto devices = findDevices(icsneo::DeviceType::FIRE3);
|
||||
// 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->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
for(auto& device : devices) {
|
||||
std::cout << "Connecting to " << device->describe() << "... ";
|
||||
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;
|
||||
|
||||
supported_nets = device->getSupportedTXNetworks();
|
||||
|
||||
device->goOnline();
|
||||
|
||||
// Demonstrate lock and unlock all networks
|
||||
std::cout << "Requesting global exclusive network mutex" << std::endl;
|
||||
auto lt = device->lockAllNetworks(MUTEX_PRIORITY, MUTEX_TTL_MS, icsneo::NetworkMutexType::TxExclusive, on_mutex_event);
|
||||
if(lt.has_value()) {
|
||||
// wait until we have the mutex on all networks
|
||||
auto all_nets = device->getSupportedTXNetworks();
|
||||
for(auto net = all_nets.begin(); net != all_nets.end();) {
|
||||
auto state = network_mutex_states.find(net->getNetID());
|
||||
if(state != network_mutex_states.end() && state->second == icsneo::NetworkMutexEvent::Acquired) {
|
||||
net = all_nets.erase(net);
|
||||
} else {
|
||||
++net;
|
||||
}
|
||||
}
|
||||
std::cout << "Acquired mutex for all supported networks on " << device->describe() << std::endl;
|
||||
|
||||
// request release
|
||||
if(device->unlockAllNetworks()) {
|
||||
std::cout << "Requested release of all networks" << std::endl;
|
||||
} else {
|
||||
std::cout << "Failed to request release of all networks" << std::endl;
|
||||
}
|
||||
|
||||
// wait until we have released the mutex on all networks
|
||||
all_nets = device->getSupportedTXNetworks();
|
||||
for(auto net = all_nets.begin(); net != all_nets.end();) {
|
||||
auto state = network_mutex_states.find(net->getNetID());
|
||||
if(state == network_mutex_states.end() || state->second == icsneo::NetworkMutexEvent::Released) {
|
||||
net = all_nets.erase(net);
|
||||
} else {
|
||||
++net;
|
||||
}
|
||||
}
|
||||
std::cout << "released mutex on all supported networks" << std::endl;
|
||||
|
||||
device->removeMessageCallback(*lt);
|
||||
} else {
|
||||
std::cout << "Failed to acquire global mutex" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Doing mutexed operation on DWCAN 01" << std::endl;
|
||||
do_mutexed_operation(device, icsneo::Network::NetID::DWCAN_01, [&](){
|
||||
auto tx_message = std::make_shared<icsneo::CANMessage>();
|
||||
tx_message->arbid = 0x12;
|
||||
tx_message->isCANFD = true;
|
||||
tx_message->baudrateSwitch = true;
|
||||
tx_message->data.resize(64);
|
||||
tx_message->network = icsneo::Network::NetID::DWCAN_01;
|
||||
constexpr auto txInterval = std::chrono::microseconds(500);
|
||||
auto nextRefresh = std::chrono::steady_clock::now() + std::chrono::milliseconds(MUTEX_TTL_MS / 2);
|
||||
auto nextTx = std::chrono::steady_clock::now() + txInterval;
|
||||
size_t numTx = 0;
|
||||
while(numTx < 5000) {
|
||||
if(std::chrono::steady_clock::now() < nextTx) {
|
||||
continue;
|
||||
}
|
||||
nextTx = std::chrono::steady_clock::now() + txInterval;
|
||||
std::iota(tx_message->data.begin(), tx_message->data.end(), (uint8_t)(numTx & 0xFF));
|
||||
if(device->transmit(tx_message)) {
|
||||
++numTx;
|
||||
}
|
||||
// check the mutex periodically - every 200 messages for demonstrative purposes
|
||||
if(std::chrono::steady_clock::now() > nextRefresh) {
|
||||
auto status = device->getNetworkMutexStatus(icsneo::Network::NetID::DWCAN_01);
|
||||
if(!status) {
|
||||
std::cout << "failed to poll status of network mutex for DWCAN 01" << std::endl;
|
||||
} else {
|
||||
std::cout << "DWCAN 01 mutex TTL: " << *status->ttlMs << " ms" << std::endl;
|
||||
}
|
||||
std::cout << "refreshing mutex on DWCAN 01, current tx count: " << numTx << std::endl;
|
||||
(void)device->lockNetworks({icsneo::Network::NetID::DWCAN_01}, MUTEX_PRIORITY, MUTEX_TTL_MS, icsneo::NetworkMutexType::TxExclusive, nullptr);
|
||||
nextRefresh = std::chrono::steady_clock::now() + std::chrono::milliseconds(MUTEX_TTL_MS / 2);
|
||||
}
|
||||
}
|
||||
});
|
||||
std::cout << "Completed mutexed operation on DWCAN 01" << std::endl;
|
||||
|
||||
|
||||
std::cout << "Doing mutexed operation on Ethernet 03" << std::endl;
|
||||
do_mutexed_operation(device, icsneo::Network::NetID::ETHERNET_03, [&](){
|
||||
auto tx_message = std::make_shared<icsneo::EthernetMessage>();
|
||||
tx_message->data.resize(512);
|
||||
// set mac addresses and ethertype
|
||||
std::iota(tx_message->data.begin(), tx_message->data.begin() + 14, static_cast<uint8_t>(0x01));
|
||||
tx_message->network = icsneo::Network::NetID::ETHERNET_03;
|
||||
constexpr auto txInterval = std::chrono::microseconds(250);
|
||||
auto nextRefresh = std::chrono::steady_clock::now() + std::chrono::milliseconds(MUTEX_TTL_MS / 2);
|
||||
auto nextTx = std::chrono::steady_clock::now() + txInterval;
|
||||
size_t numTx = 0;
|
||||
while(numTx < 50000) {
|
||||
if(std::chrono::steady_clock::now() < nextTx) {
|
||||
continue;
|
||||
}
|
||||
nextTx = std::chrono::steady_clock::now() + txInterval;
|
||||
// keep the mac addresses and ethertype constant
|
||||
std::iota(tx_message->data.begin() + 14, tx_message->data.end(), (uint8_t)(numTx & 0xFF));
|
||||
if(device->transmit(tx_message)) {
|
||||
++numTx;
|
||||
}
|
||||
if(std::chrono::steady_clock::now() > nextRefresh) {
|
||||
auto status = device->getNetworkMutexStatus(icsneo::Network::NetID::ETHERNET_03);
|
||||
if(!status) {
|
||||
std::cout << "failed to poll status of network mutex for Ethernet 03" << std::endl;
|
||||
} else {
|
||||
std::cout << "Ethernet 03 mutex TTL: " << *status->ttlMs << " ms" << std::endl;
|
||||
}
|
||||
std::cout << "refreshing mutex on Ethernet 03, current tx count: " << numTx << std::endl;
|
||||
(void)device->lockNetworks({icsneo::Network::NetID::ETHERNET_03}, MUTEX_PRIORITY, MUTEX_TTL_MS, icsneo::NetworkMutexType::TxExclusive, nullptr);
|
||||
nextRefresh = std::chrono::steady_clock::now() + std::chrono::milliseconds(MUTEX_TTL_MS / 2);
|
||||
}
|
||||
}
|
||||
});
|
||||
std::cout << "Completed mutexed operation on Ethernet 03" << std::endl;
|
||||
|
||||
device->close();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user