Add device sharing support
parent
78465e0f20
commit
a9157c82e5
|
|
@ -9,6 +9,8 @@ option(LIBICSNEO_BUILD_ICSNEOC_STATIC "Build static C library" ON)
|
|||
option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON)
|
||||
set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to build with Npcap")
|
||||
|
||||
option(LIBICSNEO_USE_DEVICE_SHARING "Interact with devices through the sharing server" ON)
|
||||
|
||||
# Device Drivers
|
||||
# You almost certainly don't want firmio for your build,
|
||||
# it is only relevant for communication between Linux and
|
||||
|
|
@ -18,13 +20,13 @@ option(LIBICSNEO_ENABLE_RAW_ETHERNET "Enable devices which communicate over raw
|
|||
option(LIBICSNEO_ENABLE_CDCACM "Enable devices which communicate over USB CDC ACM" ON)
|
||||
option(LIBICSNEO_ENABLE_FTDI "Enable devices which communicate over USB FTDI2XX" ON)
|
||||
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# Enable Warnings
|
||||
if(MSVC)
|
||||
|
|
@ -113,6 +115,11 @@ if(WIN32)
|
|||
platform/windows/vcp.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/windows/sharedmemory.cpp
|
||||
platform/windows/sharedsemaphore.cpp
|
||||
)
|
||||
else() # Darwin or Linux
|
||||
set(PLATFORM_SRC)
|
||||
|
||||
|
|
@ -155,6 +162,11 @@ else() # Darwin or Linux
|
|||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/posix/sharedmemory.cpp
|
||||
platform/posix/sharedsemaphore.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_EXAMPLES)
|
||||
|
|
@ -192,6 +204,9 @@ set(SRC_FILES
|
|||
communication/multichannelcommunication.cpp
|
||||
communication/communication.cpp
|
||||
communication/driver.cpp
|
||||
communication/interprocessmailbox.cpp
|
||||
communication/sdio.cpp
|
||||
communication/socket.cpp
|
||||
device/extensions/flexray/extension.cpp
|
||||
device/extensions/flexray/controller.cpp
|
||||
device/idevicesettings.cpp
|
||||
|
|
@ -265,8 +280,7 @@ target_include_directories(icsneocpp
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${LIBICSNEO_EXTENSION_INCLUDE_PATHS}
|
||||
)
|
||||
set_property(TARGET icsneocpp PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_features(icsneocpp PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
||||
|
||||
message("Loaded extensions: " ${LIBICSNEO_EXTENSION_TARGETS})
|
||||
target_link_libraries(icsneocpp PUBLIC ${LIBICSNEO_EXTENSION_TARGETS})
|
||||
if(LIBICSNEO_ENABLE_FIRMIO)
|
||||
|
|
@ -281,10 +295,17 @@ endif()
|
|||
if(LIBICSNEO_ENABLE_FTDI)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI)
|
||||
endif()
|
||||
if(LIBICSNEO_USE_DEVICE_SHARING)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_DEVICE_SHARING)
|
||||
endif()
|
||||
|
||||
# socket
|
||||
if(WIN32)
|
||||
target_link_libraries(icsneocpp PRIVATE ws2_32)
|
||||
endif()
|
||||
|
||||
# fatfs
|
||||
add_subdirectory(third-party/fatfs)
|
||||
set_property(TARGET fatfs PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(icsneocpp PRIVATE fatfs)
|
||||
|
||||
# libftdi
|
||||
|
|
@ -329,6 +350,11 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
|||
target_link_libraries(icsneocpp PUBLIC "-framework CoreFoundation" "-framework IOKit")
|
||||
endif()
|
||||
|
||||
# For SharedMemory and SharedSemaphore
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(icsneocpp PUBLIC rt)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_ICSNEOC)
|
||||
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
|
||||
target_include_directories(icsneoc
|
||||
|
|
@ -393,6 +419,8 @@ if(LIBICSNEO_BUILD_TESTS)
|
|||
test/diskdriverwritetest.cpp
|
||||
test/eventmanagertest.cpp
|
||||
test/ethernetpacketizertest.cpp
|
||||
test/interprocessmailboxtest.cpp
|
||||
test/sockettest.cpp
|
||||
test/i2cencoderdecodertest.cpp
|
||||
test/a2bencoderdecodertest.cpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
using namespace icsneo;
|
||||
|
||||
APIEvent::APIEvent(Type type, APIEvent::Severity severity, const Device* device) : eventStruct({}) {
|
||||
APIEvent::APIEvent(Type type, Severity severity, const Device* device) : eventStruct({}) {
|
||||
this->device = device;
|
||||
if(device) {
|
||||
serial = device->getSerial();
|
||||
|
|
@ -14,6 +14,17 @@ APIEvent::APIEvent(Type type, APIEvent::Severity severity, const Device* device)
|
|||
init(type, severity);
|
||||
}
|
||||
|
||||
APIEvent::APIEvent(neosocketevent_t evStruct, const Device* device)
|
||||
{
|
||||
this->device = device;
|
||||
timepoint = EventClock::from_time_t(evStruct.timestamp);
|
||||
serial = std::string(evStruct.serial);
|
||||
eventStruct.eventNumber = evStruct.eventNumber;
|
||||
eventStruct.severity = evStruct.severity;
|
||||
eventStruct.description = DescriptionForType(APIEvent::getType());
|
||||
eventStruct.timestamp = evStruct.timestamp;
|
||||
}
|
||||
|
||||
void APIEvent::init(Type event, APIEvent::Severity severity) {
|
||||
timepoint = EventClock::now();
|
||||
eventStruct.description = DescriptionForType(event);
|
||||
|
|
@ -45,6 +56,15 @@ std::string APIEvent::describe() const noexcept {
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
neosocketevent_t APIEvent::getNeoSocketEvent() const noexcept {
|
||||
neosocketevent_t neoSocketEvent;
|
||||
neoSocketEvent.eventNumber = eventStruct.eventNumber;
|
||||
neoSocketEvent.severity = eventStruct.severity;
|
||||
std::memcpy(neoSocketEvent.serial, eventStruct.serial, sizeof(eventStruct.serial));
|
||||
neoSocketEvent.timestamp = eventStruct.timestamp;
|
||||
return neoSocketEvent;
|
||||
}
|
||||
|
||||
void APIEvent::downgradeFromError() noexcept {
|
||||
eventStruct.severity = (uint8_t) APIEvent::Severity::EventWarning;
|
||||
}
|
||||
|
|
@ -126,8 +146,31 @@ static constexpr const char* PCAP_COULD_NOT_START = "The PCAP driver could not b
|
|||
static constexpr const char* PCAP_COULD_NOT_FIND_DEVICES = "The PCAP driver failed to find devices. Ethernet devices will not be found.";
|
||||
static constexpr const char* PACKET_DECODING = "There was an error decoding a packet from the device.";
|
||||
|
||||
static constexpr const char* SHARED_MEMORY_DATA_IS_NULL = "data() was called on invalid object.";
|
||||
static constexpr const char* SHARED_MEMORY_FAILED_TO_CLOSE = "The server failed to close a shared memory location.";
|
||||
static constexpr const char* SHARED_MEMORY_FAILED_TO_OPEN = "The server failed to open a shared memory location.";
|
||||
static constexpr const char* SHARED_MEMORY_FAILED_TO_UNLINK = "The server failed to unlink a shared memory location.";
|
||||
static constexpr const char* SHARED_MEMORY_FILE_TRUNCATE_ERROR = "The server failed to truncate shared memory file.";
|
||||
static constexpr const char* SHARED_MEMORY_MAPPING_ERROR = "The server failed to map a shared memory file.";
|
||||
static constexpr const char* SHARED_MEMORY_UNMAP_ERROR = "The server failed to unmap a shared memory file.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_CLOSE = "The server failed to close a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_OPEN = "The server failed to open a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_POST = "A post() call failed on a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_UNLINK = "The server failed to unlink a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_WAIT = "A wait() call failed on a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_NOT_OPEN_FOR_POST = "post() was called on a shared semaphore that is not open.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_NOT_OPEN_FOR_WAIT = "wait() was called on a shared semaphore that is not open.";
|
||||
static constexpr const char* SOCKET_FAILED_CONNECT = "A socket connection was attempted but failed.";
|
||||
static constexpr const char* SOCKET_FAILED_OPEN = "A socket failed to open.";
|
||||
static constexpr const char* SOCKET_FAILED_CLOSE = "A socket failed to close.";
|
||||
static constexpr const char* SOCKET_FAILED_READ = "A socket read operation failed.";
|
||||
static constexpr const char* SOCKET_FAILED_WRITE = "A socket write operation failed.";
|
||||
static constexpr const char* ACCEPTOR_FAILED_BIND = "A socket acceptor failed to bind.";
|
||||
static constexpr const char* ACCEPTOR_FAILED_LISTEN = "A socket acceptor failed to listen.";
|
||||
|
||||
static constexpr const char* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated.";
|
||||
static constexpr const char* UNKNOWN = "An unknown internal error occurred.";
|
||||
static constexpr const char* NO_ERROR_FOUND = "No errors found.";
|
||||
static constexpr const char* INVALID = "An invalid internal error occurred.";
|
||||
const char* APIEvent::DescriptionForType(Type type) {
|
||||
switch(type) {
|
||||
|
|
@ -267,11 +310,57 @@ const char* APIEvent::DescriptionForType(Type type) {
|
|||
case Type::PacketDecodingError:
|
||||
return PACKET_DECODING;
|
||||
|
||||
// Device Sharing Server Events
|
||||
case Type::SharedMemoryDataIsNull:
|
||||
return SHARED_MEMORY_DATA_IS_NULL;
|
||||
case Type::SharedMemoryFailedToClose:
|
||||
return SHARED_MEMORY_FAILED_TO_CLOSE;
|
||||
case Type::SharedMemoryFailedToOpen:
|
||||
return SHARED_MEMORY_FAILED_TO_OPEN;
|
||||
case Type::SharedMemoryFailedToUnlink:
|
||||
return SHARED_MEMORY_FAILED_TO_UNLINK;
|
||||
case Type::SharedMemoryFileTruncateError:
|
||||
return SHARED_MEMORY_FILE_TRUNCATE_ERROR;
|
||||
case Type::SharedMemoryMappingError:
|
||||
return SHARED_MEMORY_MAPPING_ERROR;
|
||||
case Type::SharedMemoryUnmapError:
|
||||
return SHARED_MEMORY_UNMAP_ERROR;
|
||||
case Type::SharedSemaphoreFailedToClose:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_CLOSE;
|
||||
case Type::SharedSemaphoreFailedToOpen:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_OPEN;
|
||||
case Type::SharedSemaphoreFailedToPost:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_POST;
|
||||
case Type::SharedSemaphoreFailedToUnlink:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_UNLINK;
|
||||
case Type::SharedSemaphoreFailedToWait:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_WAIT;
|
||||
case Type::SharedSemaphoreNotOpenForPost:
|
||||
return SHARED_SEMAPHORE_NOT_OPEN_FOR_POST;
|
||||
case Type::SharedSemaphoreNotOpenForWait:
|
||||
return SHARED_SEMAPHORE_NOT_OPEN_FOR_WAIT;
|
||||
case Type::SocketFailedToOpen:
|
||||
return SOCKET_FAILED_OPEN;
|
||||
case Type::SocketFailedToClose:
|
||||
return SOCKET_FAILED_CLOSE;
|
||||
case Type::SocketFailedToConnect:
|
||||
return SOCKET_FAILED_CONNECT;
|
||||
case Type::SocketFailedToRead:
|
||||
return SOCKET_FAILED_READ;
|
||||
case Type::SocketFailedToWrite:
|
||||
return SOCKET_FAILED_WRITE;
|
||||
case Type::SocketAcceptorFailedToBind:
|
||||
return ACCEPTOR_FAILED_BIND;
|
||||
case Type::SocketAcceptorFailedToListen:
|
||||
return ACCEPTOR_FAILED_LISTEN;
|
||||
|
||||
// Other Errors
|
||||
case Type::TooManyEvents:
|
||||
return TOO_MANY_EVENTS;
|
||||
case Type::Unknown:
|
||||
return UNKNOWN;
|
||||
case Type::NoErrorFound:
|
||||
return NO_ERROR_FOUND;
|
||||
default:
|
||||
return INVALID;
|
||||
}
|
||||
|
|
@ -292,3 +381,13 @@ bool EventFilter::match(const APIEvent& event) const noexcept {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
neosocketeventfilter_t EventFilter::getNeoSocketEventFilter() const noexcept {
|
||||
neosocketeventfilter_t filterStruct;
|
||||
filterStruct.eventNumber = static_cast<decltype(filterStruct.eventNumber)>(type);
|
||||
filterStruct.severity = static_cast<decltype(filterStruct.severity)>(severity);
|
||||
if((serial.length() + 1) == sizeof(filterStruct.serial)) {
|
||||
std::memcpy(&filterStruct.serial[0], serial.c_str(), sizeof(filterStruct.serial));
|
||||
}
|
||||
return filterStruct;
|
||||
}
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
#include "icsneo/api/eventmanager.h"
|
||||
#include "icsneo/api/event.h"
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
#include "icsneo/communication/socket.h"
|
||||
#endif
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
|
|
@ -40,7 +46,7 @@ void EventManager::add(APIEvent event) {
|
|||
if(i != downgradedThreads.end() && i->second) {
|
||||
event.downgradeFromError();
|
||||
{
|
||||
std::lock_guard<std::mutex> eventsLock(eventsMutex);
|
||||
std::lock_guard<std::mutex> eventsLock{eventsMutex};
|
||||
addEventInternal(event);
|
||||
} // free the lock so that callbacks may modify events
|
||||
runCallbacks(event);
|
||||
|
|
@ -146,23 +152,60 @@ bool EventManager::isDowngradingErrorsOnCurrentThread() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
std::optional<std::vector<neosocketevent_t>> EventManager::getServerEvents(const size_t& max)
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped<RPC>(RPC::GET_EVENTS) && socket.writeTyped<size_t>(max)))
|
||||
return std::nullopt;
|
||||
|
||||
size_t count;
|
||||
if(!socket.readTyped(count) || count < 0 || count > eventLimit)
|
||||
return std::nullopt;
|
||||
|
||||
std::optional<std::vector<neosocketevent_t>> ret;
|
||||
if(count == size_t(0))
|
||||
return ret;
|
||||
|
||||
auto& eventStructs = ret.emplace(count);
|
||||
if(!socket.read(eventStructs.data(), (eventStructs.size() * sizeof(neosocketevent_t))))
|
||||
return std::nullopt;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif // ICSNEO_ENABLE_DEVICE_SHARING
|
||||
|
||||
void EventManager::get(std::vector<APIEvent>& eventOutput, size_t max, EventFilter filter) {
|
||||
std::lock_guard<std::mutex> lk(eventsMutex);
|
||||
|
||||
if(max == 0) // A limit of 0 indicates no limit
|
||||
max = (size_t)-1;
|
||||
|
||||
size_t count = 0;
|
||||
eventOutput.clear();
|
||||
auto it = events.begin();
|
||||
while(it != events.end()) {
|
||||
if(filter.match(*it)) {
|
||||
eventOutput.push_back(*it);
|
||||
it = events.erase(it);
|
||||
if(++count >= max)
|
||||
break; // We now have as many written to output as we can
|
||||
} else {
|
||||
it++;
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto serverEvents = getServerEvents(max);
|
||||
if(serverEvents) {
|
||||
std::scoped_lock eventsLock{eventsMutex};
|
||||
for(neosocketevent_t& event : *serverEvents) {
|
||||
eventOutput.emplace_back(event);
|
||||
}
|
||||
if(max != 0u)
|
||||
max -= eventOutput.size();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
std::scoped_lock eventsLock{eventsMutex};
|
||||
{
|
||||
if(max == 0) // A limit of 0 indicates no limit
|
||||
max = (size_t)-1;
|
||||
|
||||
size_t count = 0;
|
||||
auto it = events.begin();
|
||||
while(it != events.end()) {
|
||||
if(filter.match(*it)) {
|
||||
eventOutput.push_back(*it);
|
||||
it = events.erase(it);
|
||||
if(++count >= max)
|
||||
break; // We now have as many written to output as we can
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@
|
|||
#include "icsneo/communication/message/readsettingsmessage.h"
|
||||
#include "icsneo/communication/message/versionmessage.h"
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
#include "icsneo/communication/socket.h"
|
||||
#endif
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
int Communication::messageCallbackIDCounter = 1;
|
||||
|
|
@ -67,6 +71,11 @@ bool Communication::isDisconnected() {
|
|||
return driver->isDisconnected();
|
||||
}
|
||||
|
||||
void Communication::modifyRawCallbacks(std::function<void(std::list<Communication::RawCallback>&)>&& cb) {
|
||||
std::scoped_lock lk(rawCallbacksMutex);
|
||||
cb(rawCallbacks);
|
||||
}
|
||||
|
||||
bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
|
||||
// This is here so that other communication types (like multichannel) can override it
|
||||
return rawWrite(bytes);
|
||||
|
|
@ -217,6 +226,15 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
|
|||
std::condition_variable cv;
|
||||
std::shared_ptr<Message> returnedMessage;
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
auto socket = lockSocket();
|
||||
int64_t ms = timeout.count();
|
||||
if(!(socket.writeTyped(RPC::DEVICE_LOCK) && socket.writeString(driver->device.serial) && socket.writeTyped(ms)))
|
||||
return nullptr;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
std::unique_lock<std::mutex> fnLk(syncMessageMutex); // Only allow for one sync message at a time
|
||||
std::unique_lock<std::mutex> cvLk(cvMutex); // Don't let the callback fire until we're waiting for it
|
||||
int cb = addMessageCallback(std::make_shared<MessageCallback>([&cvMutex, &returnedMessage, &cv](std::shared_ptr<Message> message) {
|
||||
|
|
@ -240,6 +258,13 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
|
|||
if(fail) // The caller's function failed, so don't return a message
|
||||
returnedMessage.reset();
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
if(!(socket.writeTyped(RPC::DEVICE_UNLOCK) && socket.writeString(driver->device.serial)))
|
||||
return nullptr;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
// Then we either will return the message we got or we will return the empty shared_ptr, caller responsible for checking
|
||||
return returnedMessage;
|
||||
}
|
||||
|
|
@ -274,6 +299,11 @@ void Communication::readTask() {
|
|||
}
|
||||
|
||||
void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes) {
|
||||
{
|
||||
std::lock_guard lk(rawCallbacksMutex);
|
||||
for(auto& cb : rawCallbacks)
|
||||
cb(readBytes);
|
||||
}
|
||||
if(redirectingRead) {
|
||||
// redirectingRead is an atomic so it can be set without acquiring a mutex
|
||||
// However, we do not clear it without the mutex. The idea is that if another
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
#include <cstring>
|
||||
#include "icsneo/communication/interprocessmailbox.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
bool InterprocessMailbox::open(const std::string& name, bool create)
|
||||
{
|
||||
if(!queuedSem.open(name + "-qs", create))
|
||||
return false;
|
||||
|
||||
if(!emptySem.open(name + "-es", create, MESSAGE_COUNT))
|
||||
return false;
|
||||
|
||||
if(!sharedMem.open(name + "-sm", BLOCK_SIZE * MESSAGE_COUNT, create))
|
||||
return false;
|
||||
|
||||
valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
InterprocessMailbox::operator bool() const
|
||||
{
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool InterprocessMailbox::close()
|
||||
{
|
||||
valid = false;
|
||||
return queuedSem.close() && emptySem.close() && sharedMem.close();
|
||||
}
|
||||
|
||||
bool InterprocessMailbox::read(void* data, LengthFieldType& messageLength, const std::chrono::milliseconds& timeout)
|
||||
{
|
||||
if(!queuedSem.wait(timeout))
|
||||
return false;
|
||||
auto it = sharedMem.data() + (index * BLOCK_SIZE);
|
||||
messageLength = *(LengthFieldType*)it;
|
||||
it += LENGTH_FIELD_SIZE;
|
||||
std::memcpy(data, it, std::min(messageLength, MAX_DATA_SIZE));
|
||||
if(!emptySem.post())
|
||||
return false;
|
||||
++index;
|
||||
index %= MESSAGE_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterprocessMailbox::write(const void* data, LengthFieldType messageLength, const std::chrono::milliseconds& timeout)
|
||||
{
|
||||
if(!emptySem.wait(timeout))
|
||||
return false; // the buffer is full and we timed out
|
||||
auto it = sharedMem.data() + (index * BLOCK_SIZE);
|
||||
*(LengthFieldType*)it = messageLength;
|
||||
it += LENGTH_FIELD_SIZE;
|
||||
std::memcpy(it, data, messageLength);
|
||||
if(!queuedSem.post())
|
||||
return false;
|
||||
++index;
|
||||
index %= MESSAGE_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
#include <cstring>
|
||||
|
||||
#include "icsneo/communication/sdio.h"
|
||||
#include "icsneo/platform/sharedsemaphore.h"
|
||||
#include "icsneo/platform/sharedmemory.h"
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/communication/socket.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
void SDIO::Find(std::vector<FoundDevice>& found) {
|
||||
auto socket = lockSocket();
|
||||
if(!socket.writeTyped(RPC::DEVICE_FINDER_FIND_ALL))
|
||||
return;
|
||||
uint16_t count;
|
||||
if(!socket.readTyped(count))
|
||||
return;
|
||||
|
||||
static constexpr auto serialSize = sizeof(FoundDevice::serial);
|
||||
std::vector<std::array<char, sizeof(FoundDevice::serial)>> serials(count);
|
||||
if(!socket.read(serials.data(), serials.size() * serialSize))
|
||||
return;
|
||||
|
||||
for(const auto& serial : serials) {
|
||||
auto& foundDevice = found.emplace_back();
|
||||
for(std::size_t i = 0; i < serialSize - 1 /* omit '\0' */; i++)
|
||||
foundDevice.serial[i] = static_cast<char>(std::toupper(serial[i]));
|
||||
foundDevice.makeDriver = [](const device_eventhandler_t& r, neodevice_t& d) {
|
||||
return std::unique_ptr<SDIO>(new SDIO(r, d));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
bool SDIO::open() {
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped(RPC::SDIO_OPEN) && socket.writeString(device.device->getSerial())))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
|
||||
{
|
||||
std::string mailboxName;
|
||||
if(!socket.readString(mailboxName))
|
||||
return false;
|
||||
if(!inboundIO.open(mailboxName))
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
std::string mailboxName;
|
||||
if(!socket.readString(mailboxName))
|
||||
return false;
|
||||
if(!outboundIO.open(mailboxName))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
readThread = std::thread(&SDIO::readTask, this);
|
||||
writeThread = std::thread(&SDIO::writeTask, this);
|
||||
|
||||
deviceOpen = true;
|
||||
|
||||
return deviceOpen;
|
||||
}
|
||||
|
||||
bool SDIO::close() {
|
||||
if(!isOpen() && !isDisconnected()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
closing = true;
|
||||
|
||||
// wait for the reader/writer threads to close
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
|
||||
// unblocks the reader/writer threads
|
||||
if(!inboundIO.close())
|
||||
return false;
|
||||
if(!outboundIO.close())
|
||||
return false;
|
||||
|
||||
if(readThread.joinable())
|
||||
readThread.join();
|
||||
|
||||
if(writeThread.joinable())
|
||||
writeThread.join();
|
||||
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!socket.writeTyped(RPC::SDIO_CLOSE))
|
||||
return false;
|
||||
if(!socket.writeString(device.device->getSerial()))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t flush;
|
||||
WriteOperation flushop;
|
||||
while(readQueue.try_dequeue(flush)) {}
|
||||
while(writeQueue.try_dequeue(flushop)) {}
|
||||
|
||||
closing = false;
|
||||
disconnected = false;
|
||||
deviceOpen = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDIO::isOpen() {
|
||||
return deviceOpen;
|
||||
}
|
||||
|
||||
void SDIO::readTask() {
|
||||
uint8_t data[MAX_DATA_SIZE];
|
||||
uint16_t messageLength;
|
||||
while(!closing) {
|
||||
if(!inboundIO.read(data, messageLength, std::chrono::milliseconds(100))) {
|
||||
if(!inboundIO)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(messageLength > 0) {
|
||||
if(messageLength > MAX_DATA_SIZE) { // split message
|
||||
std::vector<uint8_t> reassembled(messageLength);
|
||||
std::memcpy(reassembled.data(), data, MAX_DATA_SIZE);
|
||||
auto offset = reassembled.data() + MAX_DATA_SIZE;
|
||||
for(auto remaining = messageLength - MAX_DATA_SIZE; remaining > 0; remaining -= messageLength) {
|
||||
if(!inboundIO.read(offset, messageLength, std::chrono::milliseconds(10))) {
|
||||
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
offset += messageLength;
|
||||
}
|
||||
readQueue.enqueue_bulk(reassembled.data(), reassembled.size());
|
||||
} else {
|
||||
readQueue.enqueue_bulk(data, messageLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDIO::writeTask() {
|
||||
WriteOperation writeOp;
|
||||
while(!closing && !isDisconnected()) {
|
||||
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
||||
continue;
|
||||
|
||||
const auto dataSize = static_cast<LengthFieldType>(writeOp.bytes.size());
|
||||
|
||||
const auto tryWrite = [&](const void* input, LengthFieldType length) -> bool {
|
||||
for(int i = 0; i < 50; ++i) { // try to write for 5s, making sure we can close if need be
|
||||
if(outboundIO.write(input, length, std::chrono::milliseconds(100)))
|
||||
return true;
|
||||
if(!outboundIO)
|
||||
return false;
|
||||
}
|
||||
disconnected = true;
|
||||
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||
return false;
|
||||
};
|
||||
|
||||
if(!tryWrite(writeOp.bytes.data(), dataSize))
|
||||
continue;
|
||||
|
||||
if(writeOp.bytes.size() > MAX_DATA_SIZE) {
|
||||
auto offset = writeOp.bytes.data() + MAX_DATA_SIZE;
|
||||
for(LengthFieldType remaining = dataSize - MAX_DATA_SIZE; remaining > 0; ) {
|
||||
const auto toWrite = std::min(MAX_DATA_SIZE, remaining);
|
||||
if(!tryWrite(offset, toWrite))
|
||||
break;
|
||||
remaining -= toWrite;
|
||||
offset += toWrite;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
#include "icsneo/communication/socket.h"
|
||||
#include "icsneo/api/event.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
bool SocketBase::open() {
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if(::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if((sockFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
#ifdef _WIN32
|
||||
::WSACleanup();
|
||||
#endif
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
sockIsOpen = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::close() {
|
||||
#ifdef _WIN32
|
||||
if(::closesocket(sockFileDescriptor) < 0) {
|
||||
// should probably check for WSAEWOULDBLOCK as ::closesocket must be repeated to close in that case
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToClose, APIEvent::Severity::Error);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
::WSACleanup();
|
||||
#else
|
||||
// ignore ENOTCONN from ::shutdown as the peer may have already forcibly closed its socket (e.g. a crash)
|
||||
if( ((::shutdown(sockFileDescriptor, SHUT_RDWR) < 0) && (ENOTCONN != errno)) ||
|
||||
((::close(sockFileDescriptor) < 0) && (EBADF == errno)) )
|
||||
{
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToClose, APIEvent::Severity::Error);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
sockIsOpen = false;
|
||||
sockIsConnected = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::connect() {
|
||||
sockaddr_in addr = {0};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
if( (!isOpen() && !open()) ||
|
||||
(::inet_pton(addr.sin_family, "127.0.0.1", &addr.sin_addr) <= 0) ||
|
||||
(::connect(sockFileDescriptor, (sockaddr*)&addr, sizeof(addr)) < 0) )
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToConnect, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD tv = 5000u; // 5 second receive timeout but in windows
|
||||
#else
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 5u; // 5 second receive timeout
|
||||
tv.tv_usec = 0;
|
||||
setIgnoreSIGPIPE();
|
||||
#endif
|
||||
|
||||
// Set the 5 second timeout from above in the socket options
|
||||
::setsockopt(sockFileDescriptor, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
|
||||
::setsockopt(sockFileDescriptor, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
|
||||
|
||||
sockIsConnected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::isOpen() {
|
||||
return sockIsOpen;
|
||||
}
|
||||
|
||||
bool SocketBase::isConnected() {
|
||||
return sockIsConnected;
|
||||
}
|
||||
|
||||
bool SocketBase::read(void* output, std::size_t length) {
|
||||
if(!(isOpen() && isConnected()))
|
||||
return false;
|
||||
|
||||
#ifdef _WIN32
|
||||
return ::recv(sockFileDescriptor, (char*)output, (int)length, 0) > 0;
|
||||
#else
|
||||
if(::read(sockFileDescriptor, output, length) <= 0) {
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToRead, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SocketBase::write(const void* input, std::size_t length) {
|
||||
if(!(isOpen() && isConnected()))
|
||||
return false;
|
||||
|
||||
#ifdef _WIN32
|
||||
if(::send(sockFileDescriptor, (char*)input, (int)length, 0) < 0) {
|
||||
switch(WSAGetLastError()) {
|
||||
case WSAETIMEDOUT:
|
||||
case WSAENOTCONN:
|
||||
case WSAESHUTDOWN:
|
||||
case WSAECONNRESET:
|
||||
case WSAECONNABORTED:
|
||||
{
|
||||
sockIsOpen = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToWrite, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if(::write(sockFileDescriptor, input, length) < 0) {
|
||||
switch(errno) {
|
||||
case EPIPE:
|
||||
case ETIMEDOUT:
|
||||
{
|
||||
sockIsOpen = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToWrite, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::readString(std::string& str) {
|
||||
size_t length;
|
||||
if(!read(&length, sizeof(length)))
|
||||
return false;
|
||||
str.resize(length);
|
||||
if(!read(str.data(), length))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::writeString(const std::string& str) {
|
||||
size_t length = str.size();
|
||||
if(!write(&length, sizeof(length)))
|
||||
return false;
|
||||
if(!write(str.data(), length))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ActiveSocket::ActiveSocket(SocketFileDescriptor sockFD) {
|
||||
sockFileDescriptor = sockFD;
|
||||
sockIsOpen = true;
|
||||
sockIsConnected = true;
|
||||
}
|
||||
|
||||
ActiveSocket::ActiveSocket(Protocol protocol, uint16_t port) {
|
||||
this->protocol = protocol;
|
||||
this->port = port;
|
||||
}
|
||||
|
||||
ActiveSocket::~ActiveSocket() {
|
||||
if(isOpen())
|
||||
close();
|
||||
}
|
||||
|
||||
LockedSocket::LockedSocket(SocketBase& base, std::unique_lock<std::mutex>&& l) :
|
||||
SocketBase(base), lock(std::move(l)) {
|
||||
}
|
||||
|
||||
LockedSocket lockSocket() {
|
||||
static ActiveSocket socket(SocketBase::Protocol::TCP, RPC_PORT);
|
||||
if(!socket.isOpen())
|
||||
socket.open();
|
||||
if(!socket.isConnected())
|
||||
socket.connect();
|
||||
static std::mutex lock;
|
||||
return LockedSocket(socket, std::unique_lock<std::mutex>(lock));
|
||||
}
|
||||
|
||||
void SocketBase::setIgnoreSIGPIPE() {
|
||||
#ifndef _WIN32
|
||||
struct sigaction sa{};
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
::sigaction(SIGPIPE, &sa, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
Acceptor::Acceptor(Protocol protocol, uint16_t port)
|
||||
: ActiveSocket(protocol, port) {
|
||||
}
|
||||
|
||||
bool Acceptor::initialize() {
|
||||
if(open() && bind() && listen()) {
|
||||
isValid = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<ActiveSocket> Acceptor::accept()
|
||||
{
|
||||
if(!isValid)
|
||||
return nullptr;
|
||||
const SocketFileDescriptor acceptFd = ::accept(sockFileDescriptor, (sockaddr*)NULL, NULL);
|
||||
if(acceptFd < 0)
|
||||
return nullptr;
|
||||
return std::make_shared<ActiveSocket>(acceptFd);
|
||||
}
|
||||
|
||||
bool Acceptor::bind()
|
||||
{
|
||||
sockaddr_in addr = {0};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons(port);
|
||||
if( (::inet_pton(addr.sin_family, "127.0.0.1", &addr.sin_addr) <= 0) ||
|
||||
(::bind(sockFileDescriptor, (sockaddr*)&addr, sizeof(addr)) < 0) )
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketAcceptorFailedToBind, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Acceptor::listen()
|
||||
{
|
||||
if(::listen(sockFileDescriptor, UINT8_MAX) < 0) {
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketAcceptorFailedToListen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} //namespace icsneo
|
||||
|
|
@ -13,6 +13,10 @@
|
|||
#include <sstream>
|
||||
#include <chrono>
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
#include "icsneo/communication/socket.h"
|
||||
#endif
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
static const uint8_t fromBase36Table[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
|
@ -178,6 +182,15 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped(RPC::DEVICE_OPEN) && socket.writeString(getSerial())))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
APIEvent::Type attemptErr = attemptToBeginCommunication();
|
||||
if(attemptErr != APIEvent::Type::NoErrorFound) {
|
||||
// We could not communicate with the device, let's see if an extension can
|
||||
|
|
@ -199,6 +212,7 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool block = false;
|
||||
forEachExtension([&block, &flags, &handler](const std::shared_ptr<DeviceExtension>& ext) {
|
||||
|
|
@ -228,58 +242,60 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
|
|||
handleInternalMessage(message);
|
||||
}));
|
||||
|
||||
heartbeatThread = std::thread([this]() {
|
||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
if(com->driver->enableHeartbeat()) {
|
||||
heartbeatThread = std::thread([this]() {
|
||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
|
||||
MessageFilter filter;
|
||||
filter.includeInternalInAny = true;
|
||||
std::atomic<bool> receivedMessage{false};
|
||||
auto messageReceivedCallbackID = com->addMessageCallback(std::make_shared<MessageCallback>(filter, [&receivedMessage](std::shared_ptr<Message> message) {
|
||||
receivedMessage = true;
|
||||
}));
|
||||
MessageFilter filter;
|
||||
filter.includeInternalInAny = true;
|
||||
std::atomic<bool> receivedMessage{false};
|
||||
auto messageReceivedCallbackID = com->addMessageCallback(std::make_shared<MessageCallback>(filter, [&receivedMessage](std::shared_ptr<Message> message) {
|
||||
receivedMessage = true;
|
||||
}));
|
||||
|
||||
// Give the device time to get situated
|
||||
auto i = 150;
|
||||
while(!stopHeartbeatThread && i != 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
i--;
|
||||
}
|
||||
|
||||
while(!stopHeartbeatThread) {
|
||||
// Wait for 110ms for a possible heartbeat
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(110));
|
||||
if(receivedMessage) {
|
||||
receivedMessage = false;
|
||||
} else {
|
||||
// Some communication, such as the bootloader and extractor interfaces, must
|
||||
// redirect the input stream from the device as it will no longer be in the
|
||||
// packet format we expect here. As a result, status updates will not reach
|
||||
// us here and suppressDisconnects() must be used. We don't want to request
|
||||
// a status and then redirect the stream, as we'll then be polluting an
|
||||
// otherwise quiet stream. This lock makes sure suppressDisconnects() will
|
||||
// block until we've either gotten our status update or disconnected from
|
||||
// the device.
|
||||
std::lock_guard<std::mutex> lk(heartbeatMutex);
|
||||
if(heartbeatSuppressed())
|
||||
continue;
|
||||
|
||||
// No heartbeat received, request a status
|
||||
com->sendCommand(Command::RequestStatusUpdate);
|
||||
// The response should come back quickly if the com is quiet
|
||||
// Give the device time to get situated
|
||||
auto i = 150;
|
||||
while(!stopHeartbeatThread && i != 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
// Check if we got a message, and if not, if settings are being applied
|
||||
i--;
|
||||
}
|
||||
|
||||
while(!stopHeartbeatThread) {
|
||||
// Wait for 110ms for a possible heartbeat
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(110));
|
||||
if(receivedMessage) {
|
||||
receivedMessage = false;
|
||||
} else {
|
||||
if(!stopHeartbeatThread && !isDisconnected())
|
||||
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||
break;
|
||||
// Some communication, such as the bootloader and extractor interfaces, must
|
||||
// redirect the input stream from the device as it will no longer be in the
|
||||
// packet format we expect here. As a result, status updates will not reach
|
||||
// us here and suppressDisconnects() must be used. We don't want to request
|
||||
// a status and then redirect the stream, as we'll then be polluting an
|
||||
// otherwise quiet stream. This lock makes sure suppressDisconnects() will
|
||||
// block until we've either gotten our status update or disconnected from
|
||||
// the device.
|
||||
std::lock_guard<std::mutex> lk(heartbeatMutex);
|
||||
if(heartbeatSuppressed())
|
||||
continue;
|
||||
|
||||
// No heartbeat received, request a status
|
||||
com->sendCommand(Command::RequestStatusUpdate);
|
||||
// The response should come back quickly if the com is quiet
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
// Check if we got a message, and if not, if settings are being applied
|
||||
if(receivedMessage) {
|
||||
receivedMessage = false;
|
||||
} else {
|
||||
if(!stopHeartbeatThread && !isDisconnected())
|
||||
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
com->removeMessageCallback(messageReceivedCallbackID);
|
||||
});
|
||||
com->removeMessageCallback(messageReceivedCallbackID);
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -338,19 +354,36 @@ bool Device::close() {
|
|||
stopHeartbeatThread = false;
|
||||
|
||||
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onDeviceClose(); return true; });
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped(RPC::DEVICE_CLOSE) && socket.writeString(getSerial())))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return com->close();
|
||||
}
|
||||
|
||||
bool Device::goOnline() {
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped(RPC::DEVICE_GO_ONLINE) && socket.writeString(getSerial())))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
|
||||
if(!com->sendCommand(Command::EnableNetworkCommunication, true))
|
||||
return false;
|
||||
|
||||
auto startTime = std::chrono::system_clock::now();
|
||||
|
||||
ledState = LEDState::Online;
|
||||
|
||||
updateLEDState();
|
||||
|
||||
std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::Reset_Status);
|
||||
filter->includeInternalInAny = true;
|
||||
|
||||
|
|
@ -370,9 +403,13 @@ bool Device::goOnline() {
|
|||
if(failOut)
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
online = true;
|
||||
|
||||
ledState = LEDState::Online;
|
||||
|
||||
updateLEDState();
|
||||
|
||||
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOnline(); return true; });
|
||||
return true;
|
||||
}
|
||||
|
|
@ -385,15 +422,21 @@ bool Device::goOffline() {
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped(RPC::DEVICE_GO_OFFLINE) && socket.writeString(getSerial())))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
|
||||
if(!com->sendCommand(Command::EnableNetworkCommunication, false))
|
||||
return false;
|
||||
|
||||
auto startTime = std::chrono::system_clock::now();
|
||||
|
||||
ledState = (latestResetStatus && latestResetStatus->cmRunning) ? LEDState::CoreMiniRunning : LEDState::Offline;
|
||||
|
||||
updateLEDState();
|
||||
|
||||
std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::Reset_Status);
|
||||
filter->includeInternalInAny = true;
|
||||
|
||||
|
|
@ -407,6 +450,11 @@ bool Device::goOffline() {
|
|||
|
||||
com->waitForMessageSync(filter, std::chrono::milliseconds(100));
|
||||
}
|
||||
#endif
|
||||
|
||||
ledState = (latestResetStatus && latestResetStatus->cmRunning) ? LEDState::CoreMiniRunning : LEDState::Offline;
|
||||
|
||||
updateLEDState();
|
||||
|
||||
online = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
#include <array>
|
||||
#include "icsneo/device/devicefinder.h"
|
||||
#include "icsneo/platform/devices.h"
|
||||
#include "icsneo/device/founddevice.h"
|
||||
#include "icsneo/communication/sdio.h"
|
||||
#include "generated/extensions/builtin.h"
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
#include "icsneo/communication/socket.h"
|
||||
#else
|
||||
#ifdef ICSNEO_ENABLE_FIRMIO
|
||||
#include "icsneo/platform/firmio.h"
|
||||
#endif
|
||||
|
|
@ -18,6 +23,7 @@
|
|||
#ifdef ICSNEO_ENABLE_FTDI
|
||||
#include "icsneo/platform/ftdi.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
|
|
@ -43,6 +49,9 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
|||
static std::vector<FoundDevice> newDriverFoundDevices;
|
||||
newDriverFoundDevices.clear();
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
SDIO::Find(newDriverFoundDevices);
|
||||
#else
|
||||
#ifdef ICSNEO_ENABLE_FIRMIO
|
||||
FirmIO::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
|
@ -58,6 +67,7 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
|||
#ifdef ICSNEO_ENABLE_FTDI
|
||||
FTDI::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Weak because we don't want to keep devices open if they go out of scope elsewhere
|
||||
static std::vector<std::weak_ptr<Device>> foundDevices;
|
||||
|
|
@ -218,7 +228,27 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
|||
}
|
||||
|
||||
const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
static std::vector<DeviceType> supportedDevices = {
|
||||
static std::vector<DeviceType> supportedDevices;
|
||||
|
||||
if (!supportedDevices.empty())
|
||||
return supportedDevices;
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!socket.writeTyped(RPC::DEVICE_FINDER_GET_SUPORTED_DEVICES))
|
||||
return supportedDevices;
|
||||
uint16_t count;
|
||||
if(!socket.readTyped(count))
|
||||
return supportedDevices;
|
||||
std::vector<devicetype_t> devices(count);
|
||||
socket.read(devices.data(), devices.size() * sizeof(devicetype_t));
|
||||
supportedDevices.reserve(count);
|
||||
for(auto& dev : devices)
|
||||
supportedDevices.emplace_back(DeviceType(dev));
|
||||
}
|
||||
#else
|
||||
supportedDevices = {
|
||||
|
||||
#ifdef __ETHERBADGE_H_
|
||||
EtherBADGE::DEVICE_TYPE,
|
||||
|
|
@ -321,6 +351,7 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
|||
#endif
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
return supportedDevices;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,3 +25,9 @@ endif()
|
|||
|
||||
add_executable(libicsneocpp-simple-example src/SimpleExample.cpp)
|
||||
target_link_libraries(libicsneocpp-simple-example icsneocpp)
|
||||
add_executable(libicsneocpp-simple-rx src/SimpleRx.cpp)
|
||||
add_executable(libicsneocpp-simple-tx src/SimpleTx.cpp)
|
||||
target_link_libraries(libicsneocpp-simple-rx icsneocpp)
|
||||
target_link_libraries(libicsneocpp-simple-tx icsneocpp)
|
||||
add_executable(libicsneocpp-simple-events src/SimpleEvents.cpp)
|
||||
target_link_libraries(libicsneocpp-simple-events icsneocpp)
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
if(args.size() != 2) {
|
||||
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto& deviceSerial = args[1];
|
||||
|
||||
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
|
||||
for(const auto& dev : icsneo::FindAllDevices()) {
|
||||
if(serial == dev->getSerial())
|
||||
return dev;
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
std::cout << "Finding device... " << std::flush;
|
||||
const auto device = findDevice(deviceSerial);
|
||||
if(!device) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Opening device... " << std::flush;
|
||||
if(!device->open()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Going online... " << std::flush;
|
||||
if(!device->goOnline()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Getting events from server..." << std::endl;
|
||||
icsneo::EventFilter ef = {};
|
||||
auto events = icsneo::GetEvents(ef, 0UL);
|
||||
if(events.size() == 0)
|
||||
std::cout << "Event return is empty :(" << std::endl;
|
||||
|
||||
for(auto& event: events)
|
||||
std::cout << event.describe() << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -9,6 +9,12 @@ int main() {
|
|||
// Print version
|
||||
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
|
||||
|
||||
// Register an event callback so we can see any errors that come in
|
||||
icsneo::EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
icsneo::EventManager::GetInstance().addEventCallback(icsneo::EventCallback([](std::shared_ptr<icsneo::APIEvent> evt) {
|
||||
std::cerr << evt->describe() << std::endl;
|
||||
}));
|
||||
|
||||
std::cout<< "Supported devices:" << std::endl;
|
||||
for(auto& dev : icsneo::GetSupportedDevices())
|
||||
std::cout << '\t' << dev.getGenericProductName() << std::endl;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
if(args.size() != 2) {
|
||||
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto& deviceSerial = args[1];
|
||||
|
||||
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
|
||||
for(const auto& dev : icsneo::FindAllDevices()) {
|
||||
if(serial == dev->getSerial())
|
||||
return dev;
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
std::cout << "Finding device... " << std::flush;
|
||||
const auto device = findDevice(deviceSerial);
|
||||
if(!device) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Opening device... " << std::flush;
|
||||
if(!device->open()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Going online... " << std::flush;
|
||||
if(!device->goOnline()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Streaming CAN messages in, enter anything to stop..." << std::endl;
|
||||
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
|
||||
if(message->type == icsneo::Message::Type::Frame) {
|
||||
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
|
||||
if(frame->network.getType() == icsneo::Network::Type::CAN) {
|
||||
auto canMessage = std::static_pointer_cast<icsneo::CANMessage>(message);
|
||||
std::cout << '\r';
|
||||
for(auto& databyte : canMessage->data)
|
||||
std::cout << std::hex << std::setw(2) << (uint32_t)databyte << ' ';
|
||||
std::cout << std::flush;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
std::cin.ignore();
|
||||
|
||||
device->removeMessageCallback(handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
if (args.size() != 2) {
|
||||
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto& deviceSerial = args[1];
|
||||
|
||||
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
|
||||
for (const auto& dev : icsneo::FindAllDevices()) {
|
||||
if (serial == dev->getSerial())
|
||||
return dev;
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
std::cout << "Finding device... " << std::flush;
|
||||
const auto device = findDevice(deviceSerial);
|
||||
if (!device) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Opening device... " << std::flush;
|
||||
if (!device->open()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Going online... " << std::flush;
|
||||
if (!device->goOnline()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::atomic<bool> stop = false;
|
||||
|
||||
std::thread thread([&] {
|
||||
std::cin.ignore();
|
||||
std::cout << "Stopping..." << std::endl;
|
||||
stop = true;
|
||||
});
|
||||
|
||||
auto txMessage = std::make_shared<icsneo::CANMessage>();
|
||||
txMessage->network = icsneo::Network::NetID::HSCAN;
|
||||
txMessage->arbid = 0x1C5001C5;
|
||||
txMessage->data.insert(txMessage->data.begin(), sizeof(size_t), 0);
|
||||
txMessage->isExtended = true;
|
||||
txMessage->isCANFD = true;
|
||||
|
||||
std::cout << "Streaming CAN messages out, enter anything to stop..." << std::endl;
|
||||
|
||||
size_t& value = *(size_t*)txMessage->data.data();
|
||||
|
||||
while (!stop) {
|
||||
device->transmit(txMessage);
|
||||
value++;
|
||||
}
|
||||
|
||||
thread.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -4,15 +4,49 @@
|
|||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4201) // nameless struct/union
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
const char* description;
|
||||
time_t timestamp;
|
||||
uint32_t eventNumber;
|
||||
uint8_t severity;
|
||||
char serial[7];
|
||||
} neoeventcontext_t;
|
||||
|
||||
typedef struct {
|
||||
time_t timestamp;
|
||||
union {
|
||||
struct {
|
||||
uint32_t eventNumber;
|
||||
int8_t severity;
|
||||
char serial[7];
|
||||
};
|
||||
neoeventcontext_t eventContext;
|
||||
};
|
||||
} neosocketevent_t;
|
||||
|
||||
typedef neoeventcontext_t neosocketeventfilter_t;
|
||||
|
||||
typedef struct {
|
||||
const char* description;
|
||||
union {
|
||||
struct {
|
||||
time_t timestamp;
|
||||
uint32_t eventNumber;
|
||||
uint8_t severity;
|
||||
char serial[7];
|
||||
};
|
||||
neosocketevent_t socketEvent;
|
||||
};
|
||||
uint8_t reserved[16];
|
||||
} neoevent_t;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <vector>
|
||||
|
|
@ -49,6 +83,7 @@ public:
|
|||
ValueNotYetPresent = 0x1013,
|
||||
Timeout = 0x1014,
|
||||
WiVINotSupported = 0x1015,
|
||||
TestEvent = 0x1016,
|
||||
|
||||
// Device Events
|
||||
PollingMessageOverflow = 0x2000,
|
||||
|
|
@ -104,6 +139,30 @@ public:
|
|||
PCAPCouldNotFindDevices = 0x3103,
|
||||
PacketDecodingError = 0x3104,
|
||||
|
||||
// Device Sharing Server Events
|
||||
SharedMemoryDataIsNull = 0x4001,
|
||||
SharedMemoryFailedToClose = 0x4002,
|
||||
SharedMemoryFailedToOpen = 0x4003,
|
||||
SharedMemoryFailedToUnlink = 0x4004,
|
||||
SharedMemoryFileTruncateError = 0x4005,
|
||||
SharedMemoryMappingError = 0x4006,
|
||||
SharedMemoryUnmapError = 0x4007,
|
||||
SharedSemaphoreFailedToClose = 0x4008,
|
||||
SharedSemaphoreFailedToOpen = 0x4009,
|
||||
SharedSemaphoreFailedToPost = 0x4010,
|
||||
SharedSemaphoreFailedToUnlink = 0x4011,
|
||||
SharedSemaphoreFailedToWait = 0x4012,
|
||||
SharedSemaphoreNotOpenForPost = 0x4013,
|
||||
SharedSemaphoreNotOpenForWait = 0x4014,
|
||||
SocketFailedToOpen = 0x4015,
|
||||
SocketFailedToClose = 0x4016,
|
||||
SocketFailedToConnect = 0x4017,
|
||||
SocketFailedToRead = 0x4018,
|
||||
SocketFailedToWrite = 0x4019,
|
||||
SocketAcceptorFailedToBind = 0x4020,
|
||||
SocketAcceptorFailedToListen = 0x4021,
|
||||
|
||||
// Other Errors
|
||||
NoErrorFound = 0xFFFFFFFD,
|
||||
TooManyEvents = 0xFFFFFFFE,
|
||||
Unknown = 0xFFFFFFFF
|
||||
|
|
@ -117,8 +176,10 @@ public:
|
|||
|
||||
APIEvent() : eventStruct({}), serial(), timepoint(), device(nullptr) {}
|
||||
APIEvent(APIEvent::Type event, APIEvent::Severity severity, const Device* device = nullptr);
|
||||
APIEvent(neosocketevent_t evStruct, const Device* device = nullptr);
|
||||
|
||||
const neoevent_t* getNeoEvent() const noexcept { return &eventStruct; }
|
||||
neosocketevent_t getNeoSocketEvent() const noexcept;
|
||||
Type getType() const noexcept { return Type(eventStruct.eventNumber); }
|
||||
Severity getSeverity() const noexcept { return Severity(eventStruct.severity); }
|
||||
std::string getDescription() const noexcept { return std::string(eventStruct.description); }
|
||||
|
|
@ -157,8 +218,12 @@ public:
|
|||
EventFilter(const Device* device, APIEvent::Severity severity) : severity(severity), matchOnDevicePtr(true), device(device) {}
|
||||
EventFilter(std::string serial, APIEvent::Type type = APIEvent::Type::Any, APIEvent::Severity severity = APIEvent::Severity::Any) : type(type), severity(severity), serial(serial) {}
|
||||
EventFilter(std::string serial, APIEvent::Severity severity) : severity(severity), serial(serial) {}
|
||||
EventFilter(neosocketeventfilter_t evFilterSt) : type(static_cast<APIEvent::Type>(evFilterSt.eventNumber)),
|
||||
severity(static_cast<APIEvent::Severity>(evFilterSt.severity)),
|
||||
serial(std::string(evFilterSt.serial)) {}
|
||||
|
||||
bool match(const APIEvent& event) const noexcept;
|
||||
neosocketeventfilter_t getNeoSocketEventFilter() const noexcept;
|
||||
|
||||
APIEvent::Type type = APIEvent::Type::Any;
|
||||
APIEvent::Severity severity = APIEvent::Severity::Any;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <map>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include "icsneo/api/event.h"
|
||||
#include "icsneo/api/eventcallback.h"
|
||||
|
||||
|
|
@ -34,6 +35,8 @@ public:
|
|||
// If this thread exists in the map, turn off downgrading
|
||||
void cancelErrorDowngradingOnCurrentThread();
|
||||
|
||||
void removeEventMirror(const std::thread::id& id);
|
||||
|
||||
bool isDowngradingErrorsOnCurrentThread() const;
|
||||
|
||||
int addEventCallback(const EventCallback &cb);
|
||||
|
|
@ -108,6 +111,10 @@ private:
|
|||
bool enforceLimit(); // Returns whether the limit enforcement resulted in an overflow
|
||||
|
||||
void discardOldest(size_t count = 1);
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
std::optional<std::vector<neosocketevent_t>> getServerEvents(const size_t& max);
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,14 @@
|
|||
#include <thread>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class Communication {
|
||||
public:
|
||||
typedef std::function<void(std::vector<uint8_t>&)> RawCallback;
|
||||
|
||||
// Note that the Packetizer is not created by the constructor,
|
||||
// and should be done once the Communication module is in place.
|
||||
Communication(
|
||||
|
|
@ -45,6 +48,7 @@ public:
|
|||
void modeChangeIncoming() { driver->modeChangeIncoming(); }
|
||||
void awaitModeChangeComplete() { driver->awaitModeChangeComplete(); }
|
||||
bool rawWrite(const std::vector<uint8_t>& bytes) { return driver->write(bytes); }
|
||||
void modifyRawCallbacks(std::function<void(std::list<Communication::RawCallback>&)>&& cb);
|
||||
virtual bool sendPacket(std::vector<uint8_t>& bytes);
|
||||
bool redirectRead(std::function<void(std::vector<uint8_t>&&)> redirectTo);
|
||||
void clearRedirectRead();
|
||||
|
|
@ -88,6 +92,8 @@ protected:
|
|||
std::atomic<bool> redirectingRead{false};
|
||||
std::function<void(std::vector<uint8_t>&&)> redirectionFn;
|
||||
std::mutex redirectingReadMutex; // Don't allow read to be disabled while in the redirectionFn
|
||||
std::mutex rawCallbacksMutex;
|
||||
std::list<std::function<void(std::vector<uint8_t>&)>> rawCallbacks;
|
||||
std::mutex syncMessageMutex;
|
||||
|
||||
void dispatchMessage(const std::shared_ptr<Message>& msg);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include "icsneo/device/neodevice.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
|
||||
|
||||
|
|
@ -16,7 +17,7 @@ namespace icsneo {
|
|||
|
||||
class Driver {
|
||||
public:
|
||||
Driver(const device_eventhandler_t& handler) : report(handler) {}
|
||||
Driver(const device_eventhandler_t& handler, neodevice_t& forDevice) : report(handler), device(forDevice) {}
|
||||
virtual ~Driver() {}
|
||||
virtual bool open() = 0;
|
||||
virtual bool isOpen() = 0;
|
||||
|
|
@ -24,12 +25,14 @@ public:
|
|||
virtual void awaitModeChangeComplete() {}
|
||||
virtual bool isDisconnected() { return disconnected; };
|
||||
virtual bool close() = 0;
|
||||
virtual bool enableHeartbeat() const { return false; }
|
||||
bool read(std::vector<uint8_t>& bytes, size_t limit = 0);
|
||||
bool readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout = std::chrono::milliseconds(100), size_t limit = 0);
|
||||
bool write(const std::vector<uint8_t>& bytes);
|
||||
virtual bool isEthernet() const { return false; }
|
||||
|
||||
device_eventhandler_t report;
|
||||
neodevice_t& device;
|
||||
|
||||
size_t writeQueueSize = 50;
|
||||
bool writeBlocks = true; // Otherwise it just fails when the queue is full
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef __INTERPROCESSMAILBOX_H_
|
||||
#define __INTERPROCESSMAILBOX_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstdint>
|
||||
#include "icsneo/platform/sharedmemory.h"
|
||||
#include "icsneo/platform/sharedsemaphore.h"
|
||||
|
||||
static constexpr uint16_t MESSAGE_COUNT = 1024;
|
||||
static constexpr uint16_t BLOCK_SIZE = 2048;
|
||||
using LengthFieldType = uint16_t;
|
||||
static constexpr uint8_t LENGTH_FIELD_SIZE = sizeof(LengthFieldType);
|
||||
static constexpr uint16_t MAX_DATA_SIZE = BLOCK_SIZE - LENGTH_FIELD_SIZE;
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class InterprocessMailbox {
|
||||
public:
|
||||
bool open(const std::string& name, bool create = false /* create the shared resources or not */);
|
||||
bool close();
|
||||
operator bool() const;
|
||||
|
||||
// data must be large enough to hold at least MAX_DATA_SIZE
|
||||
// messageLength can be larger than MAX_DATA_SIZE if the message spans multiple blocks, only MAX_DATA_SIZE will be read
|
||||
bool read(void* data, LengthFieldType& messageLength, const std::chrono::milliseconds& timeout);
|
||||
|
||||
// if messageLength is larger than MAX_DATA_SIZE it's expected that future write() calls will send the remaining data
|
||||
bool write(const void* data, LengthFieldType messageLength, const std::chrono::milliseconds& timeout);
|
||||
private:
|
||||
icsneo::SharedSemaphore queuedSem;
|
||||
icsneo::SharedSemaphore emptySem;
|
||||
icsneo::SharedMemory sharedMem;
|
||||
unsigned index = 0; // index into messages;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef __SDIO_H_
|
||||
#define __SDIO_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/driver.h"
|
||||
#include "icsneo/communication/interprocessmailbox.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class SDIO : public Driver {
|
||||
public:
|
||||
static void Find(std::vector<FoundDevice>& found);
|
||||
|
||||
SDIO(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err, forDevice) {}
|
||||
~SDIO() { if(isOpen()) close(); }
|
||||
bool open() override;
|
||||
bool close() override;
|
||||
bool isOpen() override;
|
||||
bool enableHeartbeat() const override { return true; }
|
||||
|
||||
private:
|
||||
void readTask() override;
|
||||
void writeTask() override;
|
||||
bool deviceOpen = false;
|
||||
InterprocessMailbox outboundIO;
|
||||
InterprocessMailbox inboundIO;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
#ifndef __SOCKET_H_
|
||||
#define __SOCKET_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
typedef SOCKET SocketFileDescriptor;
|
||||
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <cerrno>
|
||||
#include <string.h>
|
||||
typedef int SocketFileDescriptor;
|
||||
#endif
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
enum class RPC {
|
||||
DEVICE_FINDER_FIND_ALL,
|
||||
DEVICE_FINDER_GET_SUPORTED_DEVICES,
|
||||
DEVICE_OPEN,
|
||||
DEVICE_GO_ONLINE,
|
||||
DEVICE_GO_OFFLINE,
|
||||
DEVICE_CLOSE,
|
||||
DEVICE_LOCK,
|
||||
DEVICE_UNLOCK,
|
||||
SDIO_OPEN,
|
||||
SDIO_CLOSE,
|
||||
GET_EVENTS,
|
||||
GET_LAST_ERROR,
|
||||
GET_EVENT_COUNT,
|
||||
DISCARD_EVENTS,
|
||||
SET_EVENT_LIMIT,
|
||||
GET_EVENT_LIMIT
|
||||
};
|
||||
|
||||
static constexpr uint16_t RPC_PORT = 54949;
|
||||
|
||||
class SocketBase {
|
||||
public:
|
||||
enum class Protocol {
|
||||
TCP = SOCK_STREAM,
|
||||
};
|
||||
|
||||
bool open();
|
||||
bool close();
|
||||
bool connect();
|
||||
bool isOpen();
|
||||
bool isConnected();
|
||||
bool read(void* output, std::size_t length);
|
||||
bool write(const void* input, std::size_t length);
|
||||
bool writeString(const std::string& str);
|
||||
bool readString(std::string& str);
|
||||
|
||||
template<typename... Ts>
|
||||
bool writeTyped(Ts... input) {
|
||||
return (... && write(&input, sizeof(input)));
|
||||
}
|
||||
|
||||
template<typename... Ts>
|
||||
bool readTyped(Ts&... output) {
|
||||
return (... && read(&output, sizeof(output)));
|
||||
}
|
||||
|
||||
protected:
|
||||
Protocol protocol;
|
||||
uint16_t port;
|
||||
SocketFileDescriptor sockFileDescriptor;
|
||||
bool sockIsOpen = false;
|
||||
bool sockIsConnected = false;
|
||||
|
||||
void setIgnoreSIGPIPE();
|
||||
};
|
||||
|
||||
// RAII Socket
|
||||
class ActiveSocket : public SocketBase {
|
||||
public:
|
||||
ActiveSocket(SocketFileDescriptor sockFD);
|
||||
ActiveSocket(Protocol protocol, uint16_t port);
|
||||
~ActiveSocket();
|
||||
};
|
||||
|
||||
// RAII Socket IO
|
||||
class LockedSocket : public SocketBase {
|
||||
public:
|
||||
LockedSocket(SocketBase& socket, std::unique_lock<std::mutex>&& lock);
|
||||
private:
|
||||
std::unique_lock<std::mutex> lock;
|
||||
};
|
||||
|
||||
class Acceptor : public ActiveSocket {
|
||||
public:
|
||||
Acceptor(Protocol protocol, uint16_t port);
|
||||
bool initialize();
|
||||
std::shared_ptr<ActiveSocket> accept();
|
||||
|
||||
private:
|
||||
bool isValid = false;
|
||||
bool bind();
|
||||
bool listen();
|
||||
};
|
||||
|
||||
LockedSocket lockSocket();
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
// Hold the length of the longest name, so that C applications can allocate memory accordingly
|
||||
// Currently the longest is "Intrepid Ethernet Evaluation Board"
|
||||
#define ICSNEO_DEVICETYPE_LONGEST_NAME (35 + 1) // Add 1 so that if someone forgets, they still have space for null terminator
|
||||
#define ICSNEO_DEVICETYPE_LONGEST_NAME (145 + 1) // Add 1 so that if someone forgets, they still have space for null terminator
|
||||
#define ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION (ICSNEO_DEVICETYPE_LONGEST_NAME + 7) // 6 character serial, plus space
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ typedef std::function< std::unique_ptr<Driver>(device_eventhandler_t err, neodev
|
|||
class FoundDevice {
|
||||
public:
|
||||
neodevice_handle_t handle = 0;
|
||||
char serial[7] = {};
|
||||
deviceserial_t serial = {};
|
||||
uint16_t productId = 0;
|
||||
driver_factory_t makeDriver;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ typedef void* devicehandle_t;
|
|||
#endif
|
||||
|
||||
typedef int32_t neodevice_handle_t;
|
||||
typedef char deviceserial_t[7];
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
|
|
@ -31,7 +32,7 @@ typedef struct {
|
|||
devicehandle_t device; // Pointer back to the C++ device object
|
||||
neodevice_handle_t handle; // Handle for use by the underlying driver
|
||||
devicetype_t type;
|
||||
char serial[7];
|
||||
deviceserial_t serial;
|
||||
} neodevice_t;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public:
|
|||
* in cdcacmlinux.cpp and cdcacmdarwin.cpp respectively
|
||||
* Other POSIX systems (BSDs, QNX, etc) will need bespoke code written in the future
|
||||
*/
|
||||
CDCACM(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {}
|
||||
CDCACM(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err, forDevice) {}
|
||||
~CDCACM();
|
||||
static void Find(std::vector<FoundDevice>& found);
|
||||
|
||||
|
|
@ -37,7 +37,6 @@ public:
|
|||
void awaitModeChangeComplete() override;
|
||||
|
||||
private:
|
||||
neodevice_t& device;
|
||||
int fd = -1;
|
||||
std::optional<ino_t> disallowedInode;
|
||||
std::atomic<bool> modeChanging{false};
|
||||
|
|
|
|||
|
|
@ -60,8 +60,6 @@ private:
|
|||
void readTask();
|
||||
void writeTask();
|
||||
bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices
|
||||
|
||||
neodevice_t& device;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ public:
|
|||
bool isOpen() override;
|
||||
bool close() override;
|
||||
bool isEthernet() const override { return true; }
|
||||
bool enableHeartbeat() const override { return true; }
|
||||
private:
|
||||
char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
|
||||
neodevice_t& device;
|
||||
uint8_t deviceMAC[6];
|
||||
bool openable = true;
|
||||
EthernetPacketizer ethPacketizer;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef __SHAREDMEMORY_POSIX_H_
|
||||
#define __SHAREDMEMORY_POSIX_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class SharedMemory {
|
||||
public:
|
||||
SharedMemory() : report(makeEventHandler()) {};
|
||||
~SharedMemory();
|
||||
bool open(const std::string& name, uint32_t size, bool create = false);
|
||||
bool close();
|
||||
uint8_t* data();
|
||||
|
||||
private:
|
||||
virtual device_eventhandler_t makeEventHandler() {
|
||||
return [](APIEvent::Type type, APIEvent::Severity severity)
|
||||
{ EventManager::GetInstance().add(type, severity); };
|
||||
}
|
||||
|
||||
device_eventhandler_t report;
|
||||
std::optional<std::string> mName;
|
||||
std::optional<std::pair<uint8_t*, uint32_t>> mData;
|
||||
std::optional<bool> mCreated;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef __SHAREDSEMAPHORE_POSIX_H_
|
||||
#define __SHAREDSEMAPHORE_POSIX_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <atomic>
|
||||
#include <semaphore.h>
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class SharedSemaphore {
|
||||
public:
|
||||
~SharedSemaphore();
|
||||
bool open(const std::string& name, bool create = false, unsigned initialCount = 0);
|
||||
bool close();
|
||||
bool wait(const std::chrono::milliseconds& timeout);
|
||||
bool post();
|
||||
|
||||
private:
|
||||
virtual device_eventhandler_t makeEventHandler() {
|
||||
return [](APIEvent::Type type, APIEvent::Severity severity)
|
||||
{ EventManager::GetInstance().add(type, severity); };
|
||||
}
|
||||
|
||||
std::atomic<bool> closing = false;
|
||||
std::atomic<bool> waiting = false;
|
||||
device_eventhandler_t report = makeEventHandler();
|
||||
std::optional<const std::string> mName;
|
||||
std::optional<sem_t*> semaphore;
|
||||
std::optional<const bool> created;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __SHAREDMEMORY_H_
|
||||
#define __SHAREDMEMORY_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "icsneo/platform/windows/sharedmemory.h"
|
||||
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#include "icsneo/platform/posix/sharedmemory.h"
|
||||
#else
|
||||
#warning "Shared memory are not supported on this platform"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __SHAREDSEMAPHORE_H_
|
||||
#define __SHAREDSEMAPHORE_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "icsneo/platform/windows/sharedsemaphore.h"
|
||||
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#include "icsneo/platform/posix/sharedsemaphore.h"
|
||||
#else
|
||||
#warning "Shared semaphores are not supported on this platform"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -24,10 +24,10 @@ public:
|
|||
bool isOpen() override;
|
||||
bool close() override;
|
||||
bool isEthernet() const override { return true; }
|
||||
bool enableHeartbeat() const override { return true; }
|
||||
private:
|
||||
const PCAPDLL& pcap;
|
||||
char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
|
||||
neodevice_t& device;
|
||||
uint8_t deviceMAC[6];
|
||||
bool openable = true;
|
||||
EthernetPacketizer ethPacketizer;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef __SHAREDMEMORY_WINDOWS_H_
|
||||
#define __SHAREDMEMORY_WINDOWS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include "icsneo/platform/windows.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class SharedMemory {
|
||||
public:
|
||||
SharedMemory() : report(makeEventHandler()) {};
|
||||
~SharedMemory();
|
||||
bool open(const std::string& name, uint32_t size, bool create = false);
|
||||
bool close();
|
||||
uint8_t* data();
|
||||
|
||||
private:
|
||||
virtual device_eventhandler_t makeEventHandler() {
|
||||
return [](APIEvent::Type type, APIEvent::Severity severity)
|
||||
{ EventManager::GetInstance().add(type, severity); };
|
||||
}
|
||||
|
||||
device_eventhandler_t report;
|
||||
std::optional<HANDLE> mHandle;
|
||||
std::optional<std::pair<uint8_t*, uint32_t>> mData;
|
||||
std::optional<bool> mCreated;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef __SHAREDSEMAPHORE_WINDOWS_H_
|
||||
#define __SHAREDSEMAPHORE_WINDOWS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include "icsneo/platform/windows.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class SharedSemaphore {
|
||||
public:
|
||||
~SharedSemaphore();
|
||||
bool open(const std::string& name, bool create = false, unsigned initialCount = 0);
|
||||
bool close();
|
||||
bool wait(const std::chrono::milliseconds& timeout);
|
||||
bool post();
|
||||
|
||||
private:
|
||||
virtual device_eventhandler_t makeEventHandler() {
|
||||
return [](APIEvent::Type type, APIEvent::Severity severity)
|
||||
{ EventManager::GetInstance().add(type, severity); };
|
||||
}
|
||||
|
||||
bool closing = false;
|
||||
device_eventhandler_t report = makeEventHandler();
|
||||
std::optional<HANDLE> semaphore;
|
||||
std::optional<bool> created;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
|
|
@ -31,7 +31,6 @@ public:
|
|||
private:
|
||||
bool open(bool fromAsync);
|
||||
bool opening = false;
|
||||
neodevice_t& device;
|
||||
|
||||
struct Detail;
|
||||
std::shared_ptr<Detail> detail;
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ void FirmIO::Find(std::vector<FoundDevice>& found) {
|
|||
memcpy(foundDevice.serial, serial->deviceSerial.c_str(), sizeof(foundDevice.serial) - 1);
|
||||
foundDevice.serial[sizeof(foundDevice.serial) - 1] = '\0';
|
||||
|
||||
foundDevice.makeDriver = [](const device_eventhandler_t& report, neodevice_t&) {
|
||||
return std::unique_ptr<Driver>(new FirmIO(report));
|
||||
foundDevice.makeDriver = [](const device_eventhandler_t& report, neodevice_t& forDevice) {
|
||||
return std::unique_ptr<Driver>(new FirmIO(report, forDevice));
|
||||
};
|
||||
|
||||
found.push_back(foundDevice);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ void FTDI::Find(std::vector<FoundDevice>& found) {
|
|||
}
|
||||
}
|
||||
|
||||
FTDI::FTDI(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {
|
||||
FTDI::FTDI(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err, forDevice) {
|
||||
openable = strlen(forDevice.serial) > 0 && device.handle >= 0 && device.handle < (neodevice_handle_t)handles.size();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ bool PCAP::IsHandleValid(neodevice_handle_t handle) {
|
|||
return (netifIndex < knownInterfaces.size());
|
||||
}
|
||||
|
||||
PCAP::PCAP(device_eventhandler_t err, neodevice_t& forDevice) : Driver(err), device(forDevice), ethPacketizer(err) {
|
||||
PCAP::PCAP(device_eventhandler_t err, neodevice_t& forDevice) : Driver(err, forDevice), ethPacketizer(err) {
|
||||
if(IsHandleValid(device.handle)) {
|
||||
iface = knownInterfaces[(device.handle >> 24) & 0xFF];
|
||||
iface.fp = nullptr; // We're going to open our own connection to the interface. This should already be nullptr but just in case.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
#include <iostream> // TODO: Remove later
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include "icsneo/platform/posix/sharedmemory.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
SharedMemory::~SharedMemory() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool SharedMemory::open(const std::string& name, uint32_t size, bool create) {
|
||||
if(create)
|
||||
shm_unlink(name.c_str());
|
||||
const auto fd = create ? shm_open(name.c_str(), O_CREAT | O_RDWR | O_EXCL, 0600) : shm_open(name.c_str(), O_RDWR, 0);
|
||||
if(fd == -1) {
|
||||
report(APIEvent::Type::SharedMemoryFailedToOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
mName.emplace(name);
|
||||
if(create && ftruncate(fd, size) == -1) {
|
||||
report(APIEvent::Type::SharedMemoryFileTruncateError, APIEvent::Severity::Error);
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
const auto shm = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if(shm == MAP_FAILED) {
|
||||
report(APIEvent::Type::SharedMemoryMappingError, APIEvent::Severity::Error);
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
mData.emplace(std::make_pair((uint8_t*)shm, size));
|
||||
mCreated.emplace(create);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemory::close() {
|
||||
bool failed = false;
|
||||
if(mData) {
|
||||
if(munmap((void*)mData->first, mData->second) == -1) {
|
||||
report(APIEvent::Type::SharedMemoryUnmapError, APIEvent::Severity::EventWarning);
|
||||
failed = true;
|
||||
}
|
||||
mData.reset();
|
||||
}
|
||||
if(mName && mCreated && *mCreated) {
|
||||
if(shm_unlink(mName->c_str()) == -1) {
|
||||
report(APIEvent::Type::SharedMemoryFailedToUnlink, APIEvent::Severity::EventWarning);
|
||||
failed = true;
|
||||
}
|
||||
mName.reset();
|
||||
}
|
||||
if(failed)
|
||||
report(APIEvent::Type::SharedMemoryFailedToClose, APIEvent::Severity::Error);
|
||||
return !failed;
|
||||
}
|
||||
|
||||
uint8_t* SharedMemory::data() {
|
||||
if(!mData) {
|
||||
report(APIEvent::Type::SharedMemoryDataIsNull, APIEvent::Severity::Error);
|
||||
return nullptr;
|
||||
}
|
||||
return mData->first;
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
#include <iostream> // TODO: Remove later
|
||||
#include <fcntl.h>
|
||||
#include "icsneo/platform/posix/sharedsemaphore.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
SharedSemaphore::~SharedSemaphore() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool SharedSemaphore::open(const std::string& name, bool create, unsigned initialCount) {
|
||||
const auto slashPrefixed = "/" + name;
|
||||
if(create)
|
||||
sem_unlink(slashPrefixed.c_str()); // try clean-up, it's fine if it errors
|
||||
const auto sem = create ? sem_open(slashPrefixed.c_str(), O_CREAT | O_EXCL, 0600, initialCount) : sem_open(slashPrefixed.c_str(), 0);
|
||||
if(sem == SEM_FAILED) {
|
||||
report(APIEvent::Type::SharedSemaphoreFailedToOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
mName.emplace(slashPrefixed);
|
||||
semaphore.emplace(sem);
|
||||
created.emplace(create);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedSemaphore::close() {
|
||||
closing = true;
|
||||
|
||||
post(); // wake any waiting
|
||||
|
||||
bool failed = false;
|
||||
if(semaphore) {
|
||||
if(sem_close(semaphore.value()) == -1) {
|
||||
report(APIEvent::Type::SharedSemaphoreFailedToClose, APIEvent::Severity::Error);
|
||||
failed = true;
|
||||
}
|
||||
semaphore.reset();
|
||||
}
|
||||
if(mName && created && *created) {
|
||||
if(sem_unlink(mName->c_str()) == -1) {
|
||||
report(APIEvent::Type::SharedSemaphoreFailedToUnlink, APIEvent::Severity::Error);
|
||||
failed = true;
|
||||
}
|
||||
mName.reset();
|
||||
}
|
||||
return !failed;
|
||||
}
|
||||
|
||||
bool SharedSemaphore::wait(const std::chrono::milliseconds& timeout) {
|
||||
if(!semaphore) {
|
||||
report(APIEvent::Type::SharedSemaphoreNotOpenForWait, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
const auto timedwait = [&]() -> bool {
|
||||
#if defined(__MACH__)
|
||||
// TODO: Quite inefficient due to Darwin's lack of sem_timedwait()
|
||||
const auto tryTill = std::chrono::steady_clock::now() + timeout;
|
||||
while (std::chrono::steady_clock::now() <= tryTill) {
|
||||
if(sem_trywait(*semaphore) == 0)
|
||||
return true;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
return false;
|
||||
#else // UNIX
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
ts.tv_sec += static_cast<unsigned int>(timeout.count() / 1000);
|
||||
ts.tv_nsec += static_cast<int>((timeout.count() % 1000) * 1000000);
|
||||
// potentially promote another second
|
||||
if(ts.tv_nsec >= 1000000000) {
|
||||
ts.tv_nsec -= 1000000000;
|
||||
++ts.tv_sec;
|
||||
}
|
||||
return sem_timedwait(*semaphore, &ts) != -1;
|
||||
#endif
|
||||
};
|
||||
if(!timedwait()) {
|
||||
if(errno == ETIMEDOUT)
|
||||
return false; // unable to lock within timeout
|
||||
if(errno != EINTR) // we don't need a warning for this
|
||||
report(APIEvent::Type::SharedSemaphoreFailedToWait, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(closing)
|
||||
return false; // we were woken by close()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedSemaphore::post() {
|
||||
if(!semaphore) {
|
||||
report(APIEvent::Type::SharedSemaphoreNotOpenForPost, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(sem_post(*semaphore) == -1) {
|
||||
report(APIEvent::Type::SharedSemaphoreFailedToPost, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -166,8 +166,8 @@ void PCAP::Find(std::vector<FoundDevice>& found) {
|
|||
memcpy(foundDevice.serial, serial->deviceSerial.c_str(), sizeof(foundDevice.serial) - 1);
|
||||
foundDevice.serial[sizeof(foundDevice.serial) - 1] = '\0';
|
||||
|
||||
foundDevice.makeDriver = [](const device_eventhandler_t& reportFn, neodevice_t& device) {
|
||||
return std::unique_ptr<Driver>(new PCAP(reportFn, device));
|
||||
foundDevice.makeDriver = [](const device_eventhandler_t& reportFn, neodevice_t& forDevice) {
|
||||
return std::unique_ptr<Driver>(new PCAP(reportFn, forDevice));
|
||||
};
|
||||
|
||||
found.push_back(foundDevice);
|
||||
|
|
@ -184,7 +184,7 @@ bool PCAP::IsHandleValid(neodevice_handle_t handle) {
|
|||
return (netifIndex < knownInterfaces.size());
|
||||
}
|
||||
|
||||
PCAP::PCAP(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice), pcap(PCAPDLL::getInstance()), ethPacketizer(err) {
|
||||
PCAP::PCAP(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err, forDevice), pcap(PCAPDLL::getInstance()), ethPacketizer(err) {
|
||||
if(IsHandleValid(device.handle)) {
|
||||
iface = knownInterfaces[(device.handle >> 24) & 0xFF];
|
||||
iface.fp = nullptr; // We're going to open our own connection to the interface. This should already be nullptr but just in case.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
#include "icsneo/api/event.h"
|
||||
#include "icsneo/platform/windows/sharedmemory.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
SharedMemory::~SharedMemory() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool SharedMemory::open(const std::string& name, uint32_t size, bool create) {
|
||||
HANDLE shm;
|
||||
if(create)
|
||||
shm = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, name.c_str());
|
||||
else
|
||||
shm = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, name.c_str());
|
||||
|
||||
if(shm == NULL) {
|
||||
report(APIEvent::Type::SharedMemoryFailedToOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
mHandle.emplace(shm);
|
||||
const auto dataStart = MapViewOfFile(shm, FILE_MAP_ALL_ACCESS, 0, 0, size);
|
||||
if(dataStart == NULL) {
|
||||
report(APIEvent::Type::SharedMemoryMappingError, APIEvent::Severity::Error);
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
mData.emplace(std::make_pair((uint8_t*)dataStart, size));
|
||||
mCreated.emplace(create);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedMemory::close() {
|
||||
bool failed = false; // TODO: need to close properly
|
||||
if(failed) {
|
||||
report(APIEvent::Type::SharedMemoryFailedToClose, APIEvent::Severity::Error);
|
||||
}
|
||||
return !failed;
|
||||
}
|
||||
|
||||
uint8_t* SharedMemory::data() {
|
||||
if(!mData) {
|
||||
report(APIEvent::Type::SharedMemoryDataIsNull, APIEvent::Severity::Error);
|
||||
return nullptr;
|
||||
}
|
||||
return mData->first;
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
#include "icsneo/platform/windows/sharedsemaphore.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
SharedSemaphore::~SharedSemaphore() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool SharedSemaphore::open(const std::string& name, bool create, unsigned initialCount) {
|
||||
HANDLE sem;
|
||||
if(create)
|
||||
sem = CreateSemaphore(NULL, initialCount, LONG_MAX, name.c_str());
|
||||
else
|
||||
sem = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, name.c_str());
|
||||
|
||||
if(sem == NULL) {
|
||||
report(APIEvent::Type::SharedSemaphoreFailedToOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
semaphore.emplace(sem);
|
||||
created.emplace(create);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedSemaphore::close() {
|
||||
if(!semaphore)
|
||||
return false;
|
||||
|
||||
closing = true;
|
||||
|
||||
post(); // wake any waiting
|
||||
|
||||
if(CloseHandle(*semaphore) == 0) {
|
||||
report(APIEvent::Type::SharedSemaphoreFailedToClose, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
semaphore.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedSemaphore::wait(const std::chrono::milliseconds& timeout) {
|
||||
if(!semaphore) {
|
||||
report(APIEvent::Type::SharedSemaphoreNotOpenForWait, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(WaitForSingleObject(*semaphore, static_cast<DWORD>(timeout.count())) != 0) {
|
||||
report(APIEvent::Type::SharedSemaphoreFailedToWait, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(closing)
|
||||
return false; // we were woken by close()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SharedSemaphore::post() {
|
||||
if(!semaphore) {
|
||||
report(APIEvent::Type::SharedSemaphoreNotOpenForPost, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(ReleaseSemaphore(*semaphore, 1, NULL) == 0) {
|
||||
report(APIEvent::Type::SharedSemaphoreFailedToPost, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -192,7 +192,7 @@ void VCP::Find(std::vector<FoundDevice>& found, std::vector<std::wstring> driver
|
|||
}
|
||||
}
|
||||
|
||||
VCP::VCP(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {
|
||||
VCP::VCP(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err, forDevice) {
|
||||
detail = std::make_shared<Detail>();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
#include <thread>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "icsneo/communication/interprocessmailbox.h"
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
static std::chrono::milliseconds TIMEOUT(10'000);
|
||||
|
||||
// ensures that the shared-memory and shared-semaphores get cleared
|
||||
TEST(InterprocessMailboxTest, CreateDestroy) {
|
||||
static constexpr auto name = "icsneo-test";
|
||||
{
|
||||
InterprocessMailbox mb;
|
||||
EXPECT_TRUE(mb.open(name, true));
|
||||
EXPECT_TRUE(mb.close());
|
||||
}
|
||||
{
|
||||
InterprocessMailbox mb;
|
||||
EXPECT_FALSE(mb.open(name)); // create == false
|
||||
auto err = icsneo::GetLastError();
|
||||
EXPECT_EQ(err.getType(), APIEvent::Type::SharedSemaphoreFailedToOpen);
|
||||
EXPECT_EQ(err.getSeverity(), APIEvent::Severity::Error);
|
||||
EXPECT_FALSE(mb.close()); // never opened successfully
|
||||
}
|
||||
}
|
||||
|
||||
// these test should really be done in separate processes
|
||||
TEST(InterprocessMailboxTest, Looping) {
|
||||
size_t data = SIZE_MAX; // just dummy data
|
||||
InterprocessMailbox in;
|
||||
InterprocessMailbox out;
|
||||
EXPECT_TRUE(in.open("icsneo-test", true));
|
||||
EXPECT_TRUE(out.open("icsneo-test"));
|
||||
for(unsigned i = 0; i < MESSAGE_COUNT * 64; ++i) {
|
||||
// "send" the message
|
||||
EXPECT_TRUE(out.write(&data, sizeof(data), TIMEOUT));
|
||||
uint8_t buff[MAX_DATA_SIZE];
|
||||
LengthFieldType read;
|
||||
EXPECT_TRUE(in.read(buff, read, TIMEOUT));
|
||||
EXPECT_EQ(*(decltype(data)*)buff, data);
|
||||
--data;
|
||||
}
|
||||
EXPECT_TRUE(in.close());
|
||||
EXPECT_TRUE(out.close());
|
||||
}
|
||||
|
||||
TEST(InterprocessMailboxTest, FasterSender) {
|
||||
std::thread sender([] {
|
||||
InterprocessMailbox out;
|
||||
EXPECT_TRUE(out.open("icsneo-test", true));
|
||||
|
||||
// nested here to spawn after the sender is set up
|
||||
std::thread receiver([] {
|
||||
InterprocessMailbox in;
|
||||
EXPECT_TRUE(in.open("icsneo-test"));
|
||||
|
||||
// wait for the sender to fill up
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
uint8_t buff[MAX_DATA_SIZE];
|
||||
LengthFieldType read;
|
||||
|
||||
// the messages should be 1, 2, 3, ..., MESSAGE_COUNT, this proves the sender waited
|
||||
for(unsigned i = 0; i < MESSAGE_COUNT; ++i) {
|
||||
EXPECT_TRUE(in.read(buff, read, TIMEOUT));
|
||||
EXPECT_EQ(*(decltype(i)*)buff, i);
|
||||
}
|
||||
|
||||
// make sure we can do it again for the rest
|
||||
for(unsigned i = MESSAGE_COUNT; i < MESSAGE_COUNT * 2; ++i) {
|
||||
EXPECT_TRUE(in.read(buff, read, TIMEOUT));
|
||||
EXPECT_EQ(*(decltype(i)*)buff, i);
|
||||
}
|
||||
EXPECT_TRUE(in.close());
|
||||
});
|
||||
|
||||
// try to send two times the count, we'll end up blocked at the end till the receiver unblocks
|
||||
for(unsigned i = 0; i < MESSAGE_COUNT * 2; ++i) {
|
||||
EXPECT_TRUE(out.write(&i, sizeof(i), TIMEOUT));
|
||||
}
|
||||
|
||||
receiver.join();
|
||||
EXPECT_TRUE(out.close());
|
||||
});
|
||||
sender.join();
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#include <array>
|
||||
#include <thread>
|
||||
#include "gtest/gtest.h"
|
||||
#include "icsneo/communication/socket.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
static constexpr uint16_t TEST_PORT = 55555;
|
||||
|
||||
TEST(DeviceSharingSocketTest, SocketOpen) {
|
||||
ActiveSocket socket(SocketBase::Protocol::TCP, TEST_PORT);
|
||||
EXPECT_TRUE(socket.open());
|
||||
EXPECT_TRUE(socket.close());
|
||||
}
|
||||
|
||||
TEST(DeviceSharingSocketTest, SocketOpenAndConnect) {
|
||||
Acceptor acceptor(SocketBase::Protocol::TCP, TEST_PORT);
|
||||
ActiveSocket socket(SocketBase::Protocol::TCP, TEST_PORT);
|
||||
acceptor.initialize();
|
||||
|
||||
EXPECT_TRUE(socket.open());
|
||||
EXPECT_TRUE(socket.connect());
|
||||
EXPECT_TRUE(socket.close());
|
||||
}
|
||||
|
||||
TEST(DeviceSharingSocketTest, SocketReadWrite) {
|
||||
std::array<uint8_t, 4> testData = {0xDE, 0xAD, 0xBE, 0xEF};
|
||||
std::array<uint8_t, 4> readData;
|
||||
Acceptor acceptor(SocketBase::Protocol::TCP, TEST_PORT);
|
||||
acceptor.initialize();
|
||||
|
||||
ActiveSocket socket(SocketBase::Protocol::TCP, TEST_PORT);
|
||||
EXPECT_TRUE(socket.open());
|
||||
EXPECT_TRUE(socket.connect());
|
||||
|
||||
auto acceptSocket = acceptor.accept();
|
||||
|
||||
EXPECT_TRUE(socket.write(testData.data(), testData.size()));
|
||||
EXPECT_TRUE(acceptSocket->read(readData.data(), readData.size()));
|
||||
EXPECT_EQ(testData, readData);
|
||||
EXPECT_TRUE(socket.close());
|
||||
EXPECT_TRUE(acceptSocket->close());
|
||||
}
|
||||
Loading…
Reference in New Issue