Add device sharing support

add-device-sharing
Kyle Schwarz 2022-12-13 11:46:32 -05:00
parent 78465e0f20
commit a9157c82e5
46 changed files with 1946 additions and 102 deletions

View File

@ -9,6 +9,8 @@ option(LIBICSNEO_BUILD_ICSNEOC_STATIC "Build static C library" ON)
option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility 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") 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 # Device Drivers
# You almost certainly don't want firmio for your build, # You almost certainly don't want firmio for your build,
# it is only relevant for communication between Linux and # 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_CDCACM "Enable devices which communicate over USB CDC ACM" ON)
option(LIBICSNEO_ENABLE_FTDI "Enable devices which communicate over USB FTDI2XX" 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) include(GNUInstallDirs)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 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 # Enable Warnings
if(MSVC) if(MSVC)
@ -113,6 +115,11 @@ if(WIN32)
platform/windows/vcp.cpp platform/windows/vcp.cpp
) )
endif() endif()
list(APPEND PLATFORM_SRC
platform/windows/sharedmemory.cpp
platform/windows/sharedsemaphore.cpp
)
else() # Darwin or Linux else() # Darwin or Linux
set(PLATFORM_SRC) set(PLATFORM_SRC)
@ -155,6 +162,11 @@ else() # Darwin or Linux
endif() endif()
endif() endif()
endif() endif()
list(APPEND PLATFORM_SRC
platform/posix/sharedmemory.cpp
platform/posix/sharedsemaphore.cpp
)
endif() endif()
if(LIBICSNEO_BUILD_EXAMPLES) if(LIBICSNEO_BUILD_EXAMPLES)
@ -192,6 +204,9 @@ set(SRC_FILES
communication/multichannelcommunication.cpp communication/multichannelcommunication.cpp
communication/communication.cpp communication/communication.cpp
communication/driver.cpp communication/driver.cpp
communication/interprocessmailbox.cpp
communication/sdio.cpp
communication/socket.cpp
device/extensions/flexray/extension.cpp device/extensions/flexray/extension.cpp
device/extensions/flexray/controller.cpp device/extensions/flexray/controller.cpp
device/idevicesettings.cpp device/idevicesettings.cpp
@ -265,8 +280,7 @@ target_include_directories(icsneocpp
${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include
${LIBICSNEO_EXTENSION_INCLUDE_PATHS} ${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}) message("Loaded extensions: " ${LIBICSNEO_EXTENSION_TARGETS})
target_link_libraries(icsneocpp PUBLIC ${LIBICSNEO_EXTENSION_TARGETS}) target_link_libraries(icsneocpp PUBLIC ${LIBICSNEO_EXTENSION_TARGETS})
if(LIBICSNEO_ENABLE_FIRMIO) if(LIBICSNEO_ENABLE_FIRMIO)
@ -281,10 +295,17 @@ endif()
if(LIBICSNEO_ENABLE_FTDI) if(LIBICSNEO_ENABLE_FTDI)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI) target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI)
endif() 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 # fatfs
add_subdirectory(third-party/fatfs) add_subdirectory(third-party/fatfs)
set_property(TARGET fatfs PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(icsneocpp PRIVATE fatfs) target_link_libraries(icsneocpp PRIVATE fatfs)
# libftdi # libftdi
@ -329,6 +350,11 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
target_link_libraries(icsneocpp PUBLIC "-framework CoreFoundation" "-framework IOKit") target_link_libraries(icsneocpp PUBLIC "-framework CoreFoundation" "-framework IOKit")
endif() endif()
# For SharedMemory and SharedSemaphore
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_link_libraries(icsneocpp PUBLIC rt)
endif()
if(LIBICSNEO_BUILD_ICSNEOC) if(LIBICSNEO_BUILD_ICSNEOC)
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc) add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
target_include_directories(icsneoc target_include_directories(icsneoc
@ -393,6 +419,8 @@ if(LIBICSNEO_BUILD_TESTS)
test/diskdriverwritetest.cpp test/diskdriverwritetest.cpp
test/eventmanagertest.cpp test/eventmanagertest.cpp
test/ethernetpacketizertest.cpp test/ethernetpacketizertest.cpp
test/interprocessmailboxtest.cpp
test/sockettest.cpp
test/i2cencoderdecodertest.cpp test/i2cencoderdecodertest.cpp
test/a2bencoderdecodertest.cpp test/a2bencoderdecodertest.cpp
) )

View File

@ -4,7 +4,7 @@
using namespace icsneo; 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; this->device = device;
if(device) { if(device) {
serial = device->getSerial(); serial = device->getSerial();
@ -14,6 +14,17 @@ APIEvent::APIEvent(Type type, APIEvent::Severity severity, const Device* device)
init(type, severity); 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) { void APIEvent::init(Type event, APIEvent::Severity severity) {
timepoint = EventClock::now(); timepoint = EventClock::now();
eventStruct.description = DescriptionForType(event); eventStruct.description = DescriptionForType(event);
@ -45,6 +56,15 @@ std::string APIEvent::describe() const noexcept {
return ss.str(); 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 { void APIEvent::downgradeFromError() noexcept {
eventStruct.severity = (uint8_t) APIEvent::Severity::EventWarning; 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* 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* 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* 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* 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."; static constexpr const char* INVALID = "An invalid internal error occurred.";
const char* APIEvent::DescriptionForType(Type type) { const char* APIEvent::DescriptionForType(Type type) {
switch(type) { switch(type) {
@ -267,11 +310,57 @@ const char* APIEvent::DescriptionForType(Type type) {
case Type::PacketDecodingError: case Type::PacketDecodingError:
return PACKET_DECODING; 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 // Other Errors
case Type::TooManyEvents: case Type::TooManyEvents:
return TOO_MANY_EVENTS; return TOO_MANY_EVENTS;
case Type::Unknown: case Type::Unknown:
return UNKNOWN; return UNKNOWN;
case Type::NoErrorFound:
return NO_ERROR_FOUND;
default: default:
return INVALID; return INVALID;
} }
@ -292,3 +381,13 @@ bool EventFilter::match(const APIEvent& event) const noexcept {
return true; 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;
}

View File

@ -1,5 +1,11 @@
#include "icsneo/api/eventmanager.h" #include "icsneo/api/eventmanager.h"
#include "icsneo/api/event.h"
#include <memory> #include <memory>
#include <thread>
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
#include "icsneo/communication/socket.h"
#endif
using namespace icsneo; using namespace icsneo;
@ -40,7 +46,7 @@ void EventManager::add(APIEvent event) {
if(i != downgradedThreads.end() && i->second) { if(i != downgradedThreads.end() && i->second) {
event.downgradeFromError(); event.downgradeFromError();
{ {
std::lock_guard<std::mutex> eventsLock(eventsMutex); std::lock_guard<std::mutex> eventsLock{eventsMutex};
addEventInternal(event); addEventInternal(event);
} // free the lock so that callbacks may modify events } // free the lock so that callbacks may modify events
runCallbacks(event); runCallbacks(event);
@ -146,14 +152,50 @@ bool EventManager::isDowngradingErrorsOnCurrentThread() const {
return false; return false;
} }
void EventManager::get(std::vector<APIEvent>& eventOutput, size_t max, EventFilter filter) { #ifdef ICSNEO_ENABLE_DEVICE_SHARING
std::lock_guard<std::mutex> lk(eventsMutex); 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) {
eventOutput.clear();
#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 if(max == 0) // A limit of 0 indicates no limit
max = (size_t)-1; max = (size_t)-1;
size_t count = 0; size_t count = 0;
eventOutput.clear();
auto it = events.begin(); auto it = events.begin();
while(it != events.end()) { while(it != events.end()) {
if(filter.match(*it)) { if(filter.match(*it)) {
@ -166,6 +208,7 @@ void EventManager::get(std::vector<APIEvent>& eventOutput, size_t max, EventFilt
} }
} }
} }
}
/** /**
* Removes the returned error from the map * Removes the returned error from the map

View File

@ -14,6 +14,10 @@
#include "icsneo/communication/message/readsettingsmessage.h" #include "icsneo/communication/message/readsettingsmessage.h"
#include "icsneo/communication/message/versionmessage.h" #include "icsneo/communication/message/versionmessage.h"
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
#include "icsneo/communication/socket.h"
#endif
using namespace icsneo; using namespace icsneo;
int Communication::messageCallbackIDCounter = 1; int Communication::messageCallbackIDCounter = 1;
@ -67,6 +71,11 @@ bool Communication::isDisconnected() {
return driver->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) { bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
// This is here so that other communication types (like multichannel) can override it // This is here so that other communication types (like multichannel) can override it
return rawWrite(bytes); return rawWrite(bytes);
@ -217,6 +226,15 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
std::condition_variable cv; std::condition_variable cv;
std::shared_ptr<Message> returnedMessage; 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> 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 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) { 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 if(fail) // The caller's function failed, so don't return a message
returnedMessage.reset(); 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 // Then we either will return the message we got or we will return the empty shared_ptr, caller responsible for checking
return returnedMessage; return returnedMessage;
} }
@ -274,6 +299,11 @@ void Communication::readTask() {
} }
void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes) { void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes) {
{
std::lock_guard lk(rawCallbacksMutex);
for(auto& cb : rawCallbacks)
cb(readBytes);
}
if(redirectingRead) { if(redirectingRead) {
// redirectingRead is an atomic so it can be set without acquiring a mutex // 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 // However, we do not clear it without the mutex. The idea is that if another

View File

@ -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;
}

View File

@ -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;
}
}
}
}

View File

@ -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

View File

@ -13,6 +13,10 @@
#include <sstream> #include <sstream>
#include <chrono> #include <chrono>
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
#include "icsneo/communication/socket.h"
#endif
using namespace icsneo; 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, 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; 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(); APIEvent::Type attemptErr = attemptToBeginCommunication();
if(attemptErr != APIEvent::Type::NoErrorFound) { if(attemptErr != APIEvent::Type::NoErrorFound) {
// We could not communicate with the device, let's see if an extension can // 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; return false;
} }
} }
#endif
bool block = false; bool block = false;
forEachExtension([&block, &flags, &handler](const std::shared_ptr<DeviceExtension>& ext) { forEachExtension([&block, &flags, &handler](const std::shared_ptr<DeviceExtension>& ext) {
@ -228,6 +242,7 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
handleInternalMessage(message); handleInternalMessage(message);
})); }));
if(com->driver->enableHeartbeat()) {
heartbeatThread = std::thread([this]() { heartbeatThread = std::thread([this]() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread(); EventManager::GetInstance().downgradeErrorsOnCurrentThread();
@ -280,6 +295,7 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
com->removeMessageCallback(messageReceivedCallbackID); com->removeMessageCallback(messageReceivedCallbackID);
}); });
}
return true; return true;
} }
@ -338,19 +354,36 @@ bool Device::close() {
stopHeartbeatThread = false; stopHeartbeatThread = false;
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onDeviceClose(); return true; }); 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(); return com->close();
} }
bool Device::goOnline() { 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)) if(!com->sendCommand(Command::EnableNetworkCommunication, true))
return false; return false;
auto startTime = std::chrono::system_clock::now(); auto startTime = std::chrono::system_clock::now();
ledState = LEDState::Online;
updateLEDState();
std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::Reset_Status); std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::Reset_Status);
filter->includeInternalInAny = true; filter->includeInternalInAny = true;
@ -370,9 +403,13 @@ bool Device::goOnline() {
if(failOut) if(failOut)
return false; return false;
} }
#endif
online = true; online = true;
ledState = LEDState::Online;
updateLEDState();
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOnline(); return true; }); forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOnline(); return true; });
return true; return true;
} }
@ -385,15 +422,21 @@ bool Device::goOffline() {
return true; 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)) if(!com->sendCommand(Command::EnableNetworkCommunication, false))
return false; return false;
auto startTime = std::chrono::system_clock::now(); 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); std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::Reset_Status);
filter->includeInternalInAny = true; filter->includeInternalInAny = true;
@ -407,6 +450,11 @@ bool Device::goOffline() {
com->waitForMessageSync(filter, std::chrono::milliseconds(100)); com->waitForMessageSync(filter, std::chrono::milliseconds(100));
} }
#endif
ledState = (latestResetStatus && latestResetStatus->cmRunning) ? LEDState::CoreMiniRunning : LEDState::Offline;
updateLEDState();
online = false; online = false;

View File

@ -1,8 +1,13 @@
#include <array>
#include "icsneo/device/devicefinder.h" #include "icsneo/device/devicefinder.h"
#include "icsneo/platform/devices.h" #include "icsneo/platform/devices.h"
#include "icsneo/device/founddevice.h" #include "icsneo/device/founddevice.h"
#include "icsneo/communication/sdio.h"
#include "generated/extensions/builtin.h" #include "generated/extensions/builtin.h"
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
#include "icsneo/communication/socket.h"
#else
#ifdef ICSNEO_ENABLE_FIRMIO #ifdef ICSNEO_ENABLE_FIRMIO
#include "icsneo/platform/firmio.h" #include "icsneo/platform/firmio.h"
#endif #endif
@ -18,6 +23,7 @@
#ifdef ICSNEO_ENABLE_FTDI #ifdef ICSNEO_ENABLE_FTDI
#include "icsneo/platform/ftdi.h" #include "icsneo/platform/ftdi.h"
#endif #endif
#endif
using namespace icsneo; using namespace icsneo;
@ -43,6 +49,9 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
static std::vector<FoundDevice> newDriverFoundDevices; static std::vector<FoundDevice> newDriverFoundDevices;
newDriverFoundDevices.clear(); newDriverFoundDevices.clear();
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
SDIO::Find(newDriverFoundDevices);
#else
#ifdef ICSNEO_ENABLE_FIRMIO #ifdef ICSNEO_ENABLE_FIRMIO
FirmIO::Find(newDriverFoundDevices); FirmIO::Find(newDriverFoundDevices);
#endif #endif
@ -58,6 +67,7 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
#ifdef ICSNEO_ENABLE_FTDI #ifdef ICSNEO_ENABLE_FTDI
FTDI::Find(newDriverFoundDevices); FTDI::Find(newDriverFoundDevices);
#endif #endif
#endif
// Weak because we don't want to keep devices open if they go out of scope elsewhere // 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; 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() { 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_ #ifdef __ETHERBADGE_H_
EtherBADGE::DEVICE_TYPE, EtherBADGE::DEVICE_TYPE,
@ -321,6 +351,7 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
#endif #endif
}; };
#endif
return supportedDevices; return supportedDevices;
} }

View File

@ -25,3 +25,9 @@ endif()
add_executable(libicsneocpp-simple-example src/SimpleExample.cpp) add_executable(libicsneocpp-simple-example src/SimpleExample.cpp)
target_link_libraries(libicsneocpp-simple-example icsneocpp) 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)

View File

@ -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;
}

View File

@ -9,6 +9,12 @@ int main() {
// Print version // Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl; 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; std::cout<< "Supported devices:" << std::endl;
for(auto& dev : icsneo::GetSupportedDevices()) for(auto& dev : icsneo::GetSupportedDevices())
std::cout << '\t' << dev.getGenericProductName() << std::endl; std::cout << '\t' << dev.getGenericProductName() << std::endl;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -4,15 +4,49 @@
#include <stdint.h> #include <stdint.h>
#include <time.h> #include <time.h>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4201) // nameless struct/union
#endif
typedef struct {
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 { typedef struct {
const char* description; const char* description;
union {
struct {
time_t timestamp; time_t timestamp;
uint32_t eventNumber; uint32_t eventNumber;
uint8_t severity; uint8_t severity;
char serial[7]; char serial[7];
};
neosocketevent_t socketEvent;
};
uint8_t reserved[16]; uint8_t reserved[16];
} neoevent_t; } neoevent_t;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#ifdef __cplusplus #ifdef __cplusplus
#include <vector> #include <vector>
@ -49,6 +83,7 @@ public:
ValueNotYetPresent = 0x1013, ValueNotYetPresent = 0x1013,
Timeout = 0x1014, Timeout = 0x1014,
WiVINotSupported = 0x1015, WiVINotSupported = 0x1015,
TestEvent = 0x1016,
// Device Events // Device Events
PollingMessageOverflow = 0x2000, PollingMessageOverflow = 0x2000,
@ -104,6 +139,30 @@ public:
PCAPCouldNotFindDevices = 0x3103, PCAPCouldNotFindDevices = 0x3103,
PacketDecodingError = 0x3104, 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, NoErrorFound = 0xFFFFFFFD,
TooManyEvents = 0xFFFFFFFE, TooManyEvents = 0xFFFFFFFE,
Unknown = 0xFFFFFFFF Unknown = 0xFFFFFFFF
@ -117,8 +176,10 @@ public:
APIEvent() : eventStruct({}), serial(), timepoint(), device(nullptr) {} APIEvent() : eventStruct({}), serial(), timepoint(), device(nullptr) {}
APIEvent(APIEvent::Type event, APIEvent::Severity severity, const Device* 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; } const neoevent_t* getNeoEvent() const noexcept { return &eventStruct; }
neosocketevent_t getNeoSocketEvent() const noexcept;
Type getType() const noexcept { return Type(eventStruct.eventNumber); } Type getType() const noexcept { return Type(eventStruct.eventNumber); }
Severity getSeverity() const noexcept { return Severity(eventStruct.severity); } Severity getSeverity() const noexcept { return Severity(eventStruct.severity); }
std::string getDescription() const noexcept { return std::string(eventStruct.description); } 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(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::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(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; bool match(const APIEvent& event) const noexcept;
neosocketeventfilter_t getNeoSocketEventFilter() const noexcept;
APIEvent::Type type = APIEvent::Type::Any; APIEvent::Type type = APIEvent::Type::Any;
APIEvent::Severity severity = APIEvent::Severity::Any; APIEvent::Severity severity = APIEvent::Severity::Any;

View File

@ -10,6 +10,7 @@
#include <map> #include <map>
#include <thread> #include <thread>
#include <algorithm> #include <algorithm>
#include <optional>
#include "icsneo/api/event.h" #include "icsneo/api/event.h"
#include "icsneo/api/eventcallback.h" #include "icsneo/api/eventcallback.h"
@ -34,6 +35,8 @@ public:
// If this thread exists in the map, turn off downgrading // If this thread exists in the map, turn off downgrading
void cancelErrorDowngradingOnCurrentThread(); void cancelErrorDowngradingOnCurrentThread();
void removeEventMirror(const std::thread::id& id);
bool isDowngradingErrorsOnCurrentThread() const; bool isDowngradingErrorsOnCurrentThread() const;
int addEventCallback(const EventCallback &cb); int addEventCallback(const EventCallback &cb);
@ -108,6 +111,10 @@ private:
bool enforceLimit(); // Returns whether the limit enforcement resulted in an overflow bool enforceLimit(); // Returns whether the limit enforcement resulted in an overflow
void discardOldest(size_t count = 1); void discardOldest(size_t count = 1);
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
std::optional<std::vector<neosocketevent_t>> getServerEvents(const size_t& max);
#endif
}; };
} }

View File

@ -21,11 +21,14 @@
#include <thread> #include <thread>
#include <queue> #include <queue>
#include <map> #include <map>
#include <list>
namespace icsneo { namespace icsneo {
class Communication { class Communication {
public: public:
typedef std::function<void(std::vector<uint8_t>&)> RawCallback;
// Note that the Packetizer is not created by the constructor, // Note that the Packetizer is not created by the constructor,
// and should be done once the Communication module is in place. // and should be done once the Communication module is in place.
Communication( Communication(
@ -45,6 +48,7 @@ public:
void modeChangeIncoming() { driver->modeChangeIncoming(); } void modeChangeIncoming() { driver->modeChangeIncoming(); }
void awaitModeChangeComplete() { driver->awaitModeChangeComplete(); } void awaitModeChangeComplete() { driver->awaitModeChangeComplete(); }
bool rawWrite(const std::vector<uint8_t>& bytes) { return driver->write(bytes); } 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); virtual bool sendPacket(std::vector<uint8_t>& bytes);
bool redirectRead(std::function<void(std::vector<uint8_t>&&)> redirectTo); bool redirectRead(std::function<void(std::vector<uint8_t>&&)> redirectTo);
void clearRedirectRead(); void clearRedirectRead();
@ -88,6 +92,8 @@ protected:
std::atomic<bool> redirectingRead{false}; std::atomic<bool> redirectingRead{false};
std::function<void(std::vector<uint8_t>&&)> redirectionFn; std::function<void(std::vector<uint8_t>&&)> redirectionFn;
std::mutex redirectingReadMutex; // Don't allow read to be disabled while in the 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; std::mutex syncMessageMutex;
void dispatchMessage(const std::shared_ptr<Message>& msg); void dispatchMessage(const std::shared_ptr<Message>& msg);

View File

@ -9,6 +9,7 @@
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>
#include "icsneo/device/neodevice.h"
#include "icsneo/api/eventmanager.h" #include "icsneo/api/eventmanager.h"
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h" #include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
@ -16,7 +17,7 @@ namespace icsneo {
class Driver { class Driver {
public: 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 ~Driver() {}
virtual bool open() = 0; virtual bool open() = 0;
virtual bool isOpen() = 0; virtual bool isOpen() = 0;
@ -24,12 +25,14 @@ public:
virtual void awaitModeChangeComplete() {} virtual void awaitModeChangeComplete() {}
virtual bool isDisconnected() { return disconnected; }; virtual bool isDisconnected() { return disconnected; };
virtual bool close() = 0; virtual bool close() = 0;
virtual bool enableHeartbeat() const { return false; }
bool read(std::vector<uint8_t>& bytes, size_t limit = 0); 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 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); bool write(const std::vector<uint8_t>& bytes);
virtual bool isEthernet() const { return false; } virtual bool isEthernet() const { return false; }
device_eventhandler_t report; device_eventhandler_t report;
neodevice_t& device;
size_t writeQueueSize = 50; size_t writeQueueSize = 50;
bool writeBlocks = true; // Otherwise it just fails when the queue is full bool writeBlocks = true; // Otherwise it just fails when the queue is full

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -3,7 +3,7 @@
// Hold the length of the longest name, so that C applications can allocate memory accordingly // Hold the length of the longest name, so that C applications can allocate memory accordingly
// Currently the longest is "Intrepid Ethernet Evaluation Board" // 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 #define ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION (ICSNEO_DEVICETYPE_LONGEST_NAME + 7) // 6 character serial, plus space
#ifndef __cplusplus #ifndef __cplusplus

View File

@ -11,7 +11,7 @@ typedef std::function< std::unique_ptr<Driver>(device_eventhandler_t err, neodev
class FoundDevice { class FoundDevice {
public: public:
neodevice_handle_t handle = 0; neodevice_handle_t handle = 0;
char serial[7] = {}; deviceserial_t serial = {};
uint16_t productId = 0; uint16_t productId = 0;
driver_factory_t makeDriver; driver_factory_t makeDriver;
}; };

View File

@ -18,6 +18,7 @@ typedef void* devicehandle_t;
#endif #endif
typedef int32_t neodevice_handle_t; typedef int32_t neodevice_handle_t;
typedef char deviceserial_t[7];
#pragma pack(push, 1) #pragma pack(push, 1)
@ -31,7 +32,7 @@ typedef struct {
devicehandle_t device; // Pointer back to the C++ device object devicehandle_t device; // Pointer back to the C++ device object
neodevice_handle_t handle; // Handle for use by the underlying driver neodevice_handle_t handle; // Handle for use by the underlying driver
devicetype_t type; devicetype_t type;
char serial[7]; deviceserial_t serial;
} neodevice_t; } neodevice_t;
#pragma pack(pop) #pragma pack(pop)

View File

@ -25,7 +25,7 @@ public:
* in cdcacmlinux.cpp and cdcacmdarwin.cpp respectively * in cdcacmlinux.cpp and cdcacmdarwin.cpp respectively
* Other POSIX systems (BSDs, QNX, etc) will need bespoke code written in the future * 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(); ~CDCACM();
static void Find(std::vector<FoundDevice>& found); static void Find(std::vector<FoundDevice>& found);
@ -37,7 +37,6 @@ public:
void awaitModeChangeComplete() override; void awaitModeChangeComplete() override;
private: private:
neodevice_t& device;
int fd = -1; int fd = -1;
std::optional<ino_t> disallowedInode; std::optional<ino_t> disallowedInode;
std::atomic<bool> modeChanging{false}; std::atomic<bool> modeChanging{false};

View File

@ -60,8 +60,6 @@ private:
void readTask(); void readTask();
void writeTask(); void writeTask();
bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices
neodevice_t& device;
}; };
} }

View File

@ -24,9 +24,9 @@ public:
bool isOpen() override; bool isOpen() override;
bool close() override; bool close() override;
bool isEthernet() const override { return true; } bool isEthernet() const override { return true; }
bool enableHeartbeat() const override { return true; }
private: private:
char errbuf[PCAP_ERRBUF_SIZE] = { 0 }; char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
neodevice_t& device;
uint8_t deviceMAC[6]; uint8_t deviceMAC[6];
bool openable = true; bool openable = true;
EthernetPacketizer ethPacketizer; EthernetPacketizer ethPacketizer;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -24,10 +24,10 @@ public:
bool isOpen() override; bool isOpen() override;
bool close() override; bool close() override;
bool isEthernet() const override { return true; } bool isEthernet() const override { return true; }
bool enableHeartbeat() const override { return true; }
private: private:
const PCAPDLL& pcap; const PCAPDLL& pcap;
char errbuf[PCAP_ERRBUF_SIZE] = { 0 }; char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
neodevice_t& device;
uint8_t deviceMAC[6]; uint8_t deviceMAC[6];
bool openable = true; bool openable = true;
EthernetPacketizer ethPacketizer; EthernetPacketizer ethPacketizer;

View File

@ -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

View File

@ -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

View File

@ -31,7 +31,6 @@ public:
private: private:
bool open(bool fromAsync); bool open(bool fromAsync);
bool opening = false; bool opening = false;
neodevice_t& device;
struct Detail; struct Detail;
std::shared_ptr<Detail> detail; std::shared_ptr<Detail> detail;

View File

@ -65,8 +65,8 @@ void FirmIO::Find(std::vector<FoundDevice>& found) {
memcpy(foundDevice.serial, serial->deviceSerial.c_str(), sizeof(foundDevice.serial) - 1); memcpy(foundDevice.serial, serial->deviceSerial.c_str(), sizeof(foundDevice.serial) - 1);
foundDevice.serial[sizeof(foundDevice.serial) - 1] = '\0'; foundDevice.serial[sizeof(foundDevice.serial) - 1] = '\0';
foundDevice.makeDriver = [](const device_eventhandler_t& report, neodevice_t&) { foundDevice.makeDriver = [](const device_eventhandler_t& report, neodevice_t& forDevice) {
return std::unique_ptr<Driver>(new FirmIO(report)); return std::unique_ptr<Driver>(new FirmIO(report, forDevice));
}; };
found.push_back(foundDevice); found.push_back(foundDevice);

View File

@ -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(); openable = strlen(forDevice.serial) > 0 && device.handle >= 0 && device.handle < (neodevice_handle_t)handles.size();
} }

View File

@ -200,7 +200,7 @@ bool PCAP::IsHandleValid(neodevice_handle_t handle) {
return (netifIndex < knownInterfaces.size()); 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)) { if(IsHandleValid(device.handle)) {
iface = knownInterfaces[(device.handle >> 24) & 0xFF]; 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. iface.fp = nullptr; // We're going to open our own connection to the interface. This should already be nullptr but just in case.

View File

@ -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;
}

View File

@ -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;
}

View File

@ -166,8 +166,8 @@ void PCAP::Find(std::vector<FoundDevice>& found) {
memcpy(foundDevice.serial, serial->deviceSerial.c_str(), sizeof(foundDevice.serial) - 1); memcpy(foundDevice.serial, serial->deviceSerial.c_str(), sizeof(foundDevice.serial) - 1);
foundDevice.serial[sizeof(foundDevice.serial) - 1] = '\0'; foundDevice.serial[sizeof(foundDevice.serial) - 1] = '\0';
foundDevice.makeDriver = [](const device_eventhandler_t& reportFn, neodevice_t& device) { foundDevice.makeDriver = [](const device_eventhandler_t& reportFn, neodevice_t& forDevice) {
return std::unique_ptr<Driver>(new PCAP(reportFn, device)); return std::unique_ptr<Driver>(new PCAP(reportFn, forDevice));
}; };
found.push_back(foundDevice); found.push_back(foundDevice);
@ -184,7 +184,7 @@ bool PCAP::IsHandleValid(neodevice_handle_t handle) {
return (netifIndex < knownInterfaces.size()); 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)) { if(IsHandleValid(device.handle)) {
iface = knownInterfaces[(device.handle >> 24) & 0xFF]; 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. iface.fp = nullptr; // We're going to open our own connection to the interface. This should already be nullptr but just in case.

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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>(); detail = std::make_shared<Detail>();
} }

View File

@ -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();
}

View File

@ -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());
}