This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,73 @@
PROJECT(openvibe-module-socket)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp src/*.inl include/*.h include/*.hpp)
INCLUDE_DIRECTORIES(include/socket)
INCLUDE("FindSourceRCProperties")
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DSocket_Shared -DSocket_Exports")
ADD_LIBRARY(${PROJECT_NAME}-static STATIC ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DSocket_Static -DSocket_Exports")
IF(UNIX)
SET_PROPERTY(TARGET ${PROJECT_NAME}-static APPEND_STRING PROPERTY COMPILE_FLAGS " -fPIC")
ELSEIF(WIN32)
# SET_PROPERTY(TARGET ${PROJECT_NAME}-static APPEND_STRING PROPERTY COMPILE_FLAGS " /SAFESEH:NO")
# SET_PROPERTY(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY COMPILE_FLAGS " /SAFESEH:NO")
ENDIF()
INCLUDE("FindOpenViBECommon")
#INCLUDE("FindThirdPartyTVicPort")
# ---------------------------------
# Finds standard library winsock
# Adds library to target
# Adds include path
# ---------------------------------
IF(WIN32)
INCLUDE("OvSetWindowsSDKPath")
FIND_LIBRARY(LIB_STANDARD_MODULE_WINSOCK ws2_32 ${OV_MS_SDK_PATH}/lib)
IF(LIB_STANDARD_MODULE_WINSOCK)
MESSAGE(STATUS " Found ws2_32...")
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIB_STANDARD_MODULE_WINSOCK})
TARGET_LINK_LIBRARIES(${PROJECT_NAME}-static ${LIB_STANDARD_MODULE_WINSOCK})
ELSE(LIB_STANDARD_MODULE_WINSOCK)
MESSAGE(STATUS " FAILED to find ws2_32...")
ENDIF(LIB_STANDARD_MODULE_WINSOCK)
ENDIF(WIN32)
# ---------------------------------
# Target macros
# Defines target operating system, architecture and compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(TARGETS ${PROJECT_NAME}-static
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY include/ DESTINATION ${DIST_INCLUDEDIR} FILES_MATCHING PATTERN "*.h")
@@ -0,0 +1,34 @@
#pragma once
#include "defines.h"
#include <cstdlib> // For Unix Compatibility
namespace Socket {
class Socket_API IConnection
{
protected:
virtual bool open() = 0;
public:
virtual bool close() = 0;
virtual bool isReadyToSend(const size_t timeOut = 0) const = 0;
virtual bool isReadyToReceive(const size_t timeOut = 0) const = 0;
virtual size_t sendBuffer(const void* buffer, const size_t size) = 0;
virtual size_t receiveBuffer(void* buffer, const size_t size) = 0;
virtual bool sendBufferBlocking(const void* buffer, const size_t size) = 0;
virtual bool receiveBufferBlocking(void* buffer, const size_t size) = 0;
virtual bool isConnected() const = 0;
virtual void release() = 0;
protected:
virtual ~IConnection() = default;
};
} // namespace Socket
@@ -0,0 +1,90 @@
#pragma once
#include "IConnection.h"
#include <array>
#include <cstdio>
namespace Socket {
class Socket_API IConnectionBluetooth : public IConnection
{
public:
/**
* \brief Connect to the bluetooth device.
* \param[in] address the MAC address of the Bluetooth device.
* \return If the function succeeds, the return value is true, else false.
*/
virtual bool connect(const uint64_t address) = 0;
/**
* \brief Return the input serial pending byte count.
* \return The number of pending byte.
*/
virtual size_t getPendingByteCount() = 0;
/**
* \brief Flush the input serial buffer.
* \return If the function succeeds, the return value is true, else false.
*/
virtual const char* getLastError() const = 0;
/**
* \brief Clear the last error registered.
*/
virtual void clearError() = 0;
/**
* \brief List the paired bluetooth devices: Name and Bluetooth MAC address.
*
* \param[out] nPairedDevices the Bluetooth devices count.
* \param[out] names an array of Bluetooth names.
* \param[out] addresses an array of Bluetooth addresses.
*
* \return If the function succeeds, the return value is true, else false.
*/
virtual bool listPairedBluetoothDevices(size_t* nPairedDevices, char** names, uint64_t** addresses) = 0;
/**
* \brief Convert string MAC Bluetooth address to hexadecimal.
* \param[out] straddr string MAC Bluetooth address.
* \param[out] btaddr hexadecimal MAC Bluetooth address.
* \retval true in case of success.
* \retval false in case of failure: if the address does not match: %02x:%02x:%02x:%02x:%02x:%02x
*/
static bool string2BluetoothAddress(const char* straddr, uint64_t* btaddr)
{
std::array<uint32_t, 6> aaddr = { 0, 0, 0, 0, 0, 0 };
const int value = sscanf(straddr, "%02x:%02x:%02x:%02x:%02x:%02x", &aaddr[0], &aaddr[1], &aaddr[2], &aaddr[3], &aaddr[4], &aaddr[5]);
if (value != 6) { return false; }
*btaddr = 0;
for (size_t i = 0; i < 6; ++i)
{
const uint64_t tmpaddr = static_cast<uint64_t>(aaddr[i] & 0xff);
*btaddr = (*btaddr << 8) + tmpaddr;
}
return true;
}
protected:
#if defined TARGET_OS_Windows
static const unsigned char WIN_SOCKET_MAJOR_VERSION = 2; // Winsock major version to use
static const unsigned char WIN_SOCKET_MINOR_VERSION = 2; // Winsock minor version to use
static bool m_isWinsockInitialized;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#endif
};
extern Socket_API IConnectionBluetooth* createConnectionBluetooth();
} // namespace Socket
@@ -0,0 +1,14 @@
#pragma once
#include "IConnection.h"
namespace Socket {
class Socket_API IConnectionClient : public IConnection
{
public:
virtual bool connect(const char* serverName, const size_t serverPort, const size_t timeOut = 0xffffffff) = 0;
};
extern Socket_API IConnectionClient* createConnectionClient();
} // namespace Socket
@@ -0,0 +1,41 @@
#pragma once
#include "IConnection.h"
#include <string>
#if defined TARGET_OS_Windows
#include <windows.h>
#endif
namespace Socket {
/**
* \brief The IConnectionParallel class provides the possibility to communicate with a parallel port.
* On Windows, you must have TVicPort library installed (available for free: http://entechtaiwan.com/dev/port/index.shtm).
*/
class Socket_API IConnectionParallel : public IConnection
{
public:
virtual bool connect(const unsigned short port) = 0;
virtual std::string getLastError() = 0;
protected:
#if defined TARGET_OS_Windows
typedef bool (CALLBACK * port_open_t)();
typedef void (CALLBACK * port_close_t)();
typedef bool (CALLBACK * port_is_driver_opened_t)();
typedef bool (CALLBACK * port_write_t)(unsigned short, unsigned char);
HMODULE m_port = nullptr;
port_open_t m_portOpen = nullptr;
port_close_t m_portClose = nullptr;
port_is_driver_opened_t m_isDriverOpened = nullptr;
port_write_t m_portWrite = nullptr;
#endif
};
extern Socket_API IConnectionParallel* createConnectionParallel();
} // namespace Socket
@@ -0,0 +1,54 @@
#pragma once
#include "IConnection.h"
#include <string>
namespace Socket {
class Socket_API IConnectionSerial : public IConnection
{
public:
virtual bool connect(const char* url, const size_t baudRate) = 0;
/**
* \brief Return the input serial pending byte count.
* \return The number of pending byte.
*/
virtual size_t getPendingByteCount() = 0;
/**
* \brief Flush the input serial buffer.
* \return If the function succeeds, the return value is true, else false.
*/
virtual bool flush() = 0;
/**
* \brief Flush the input serial buffer.
* \return If the function succeeds, the return value is true, else false.
*/
virtual const char* getLastError() = 0;
/**
* \brief Checks if an error raised.
* \return True if the m_LastError is not empty, else false.
*/
virtual bool isErrorRaised() = 0;
/**
* \brief Clear the last error registered.
*/
virtual void clearError() = 0;
/**
* \brief Set tiemouts for read and write function.
* \param timeout [in] Timeout in deciseconds.
* \return True if succeed, else false.
*/
virtual bool setTimeouts(const size_t timeout) = 0;
protected:
void saveLastError();
};
extern Socket_API IConnectionSerial* createConnectionSerial();
} // namespace Socket
@@ -0,0 +1,34 @@
#pragma once
#include "TConnectionDelegate.h"
#include "IConnectionSerial.h"
namespace Socket {
struct Socket_API SConnectionSerialDelegate
{
SConnectionDelegate connectionDelegate;
bool (*fpConnect)(void*, const char*, const size_t);
size_t (*fpGetPendingByteCount)(void*);
bool (*fpFlush)(void*);
const char* (*fpGetLastError)(void*);
void (*fpSaveLastError)(void*);
// TODO for Android compatibility
//bool(*fpIsErrorRaised)(void*);
//void(*fpClearError)(void*);
//bool(*fSetTimeouts)(void*, const size_t decisecondsTimeout);
};
class Socket_API IConnectionSerialDelegate : public TConnectionDelegate<IConnectionSerial>
{
public:
IConnectionSerialDelegate(const SConnectionSerialDelegate connectionSerialDelegate)
: TConnectionDelegate<IConnectionSerial>(connectionSerialDelegate.connectionDelegate) { }
~IConnectionSerialDelegate() override { }
protected:
SConnectionSerialDelegate m_connectionSerialDelegate;
};
extern Socket_API IConnectionSerialDelegate* createConnectionSerialDelegate(SConnectionSerialDelegate connectionSerialDelegate);
} // namespace Socket
@@ -0,0 +1,29 @@
#pragma once
#include "IConnection.h"
namespace Socket {
class Socket_API IConnectionServer : public IConnection
{
public:
/*
* \brief Places a socket in a listening state on the specified port.
* \param port [in]: port on the one the socket should listen.
* when set to '0', the socket wil start on
* an available port.
*/
virtual bool listen(const size_t port) = 0;
virtual IConnection* accept() = 0;
/*
* \brief Returns the port on the one the server is listening.
* This is useful if you set the port to '0'.
* \param port [out]: port on the one the server is listening
*/
virtual bool getSocketPort(size_t& port) = 0;
};
extern Socket_API IConnectionServer* createConnectionServer();
} // namespace Socket
@@ -0,0 +1,54 @@
#pragma once
#include "IConnection.h"
namespace Socket {
struct SConnectionDelegate
{
void* data;
bool (*fpOpen)(void*);
bool (*fpClose)(void*);
bool (*fpIsReadyToSend)(void*, size_t);
bool (*fpIsReadyToReceive)(void*, size_t);
size_t (*fpSendBuffer)(void*, const void*, size_t);
size_t (*fpReceiveBuffer)(void*, void*, size_t);
bool (*fpSendBufferBlocking)(void*, const void*, size_t);
bool (*fpReceiveBufferBlocking)(void*, void*, size_t);
bool (*fpIsConnected)(void*);
bool (*fpRelease)(void*);
};
template <class T>
class Socket_API TConnectionDelegate : public T
{
public:
TConnectionDelegate(const SConnectionDelegate connectionDelegate) : m_connectionDelegate(connectionDelegate) { }
virtual bool close() { return m_connectionDelegate.fpClose(m_connectionDelegate.data); }
virtual bool isReadyToSend(const size_t timeOut) const { return m_connectionDelegate.fpIsReadyToSend(m_connectionDelegate.data, timeOut); }
virtual bool isReadyToReceive(const size_t timeOut) const { return m_connectionDelegate.fpIsReadyToReceive(m_connectionDelegate.data, timeOut); }
virtual size_t sendBuffer(const void* buffer, const size_t size) { return m_connectionDelegate.fpSendBuffer(m_connectionDelegate.data, buffer, size); }
virtual size_t receiveBuffer(void* buffer, const size_t size) { return m_connectionDelegate.fpReceiveBuffer(m_connectionDelegate.data, buffer, size); }
virtual bool sendBufferBlocking(const void* buffer, const size_t size)
{
return m_connectionDelegate.fpSendBufferBlocking(m_connectionDelegate.data, buffer, size);
}
virtual bool receiveBufferBlocking(void* buffer, const size_t bufferSize)
{
return m_connectionDelegate.fpReceiveBufferBlocking(m_connectionDelegate.data, buffer, bufferSize);
}
virtual bool isConnected() const { return m_connectionDelegate.fpIsConnected(m_connectionDelegate.data); }
virtual void release() { m_connectionDelegate.fpRelease(m_connectionDelegate.data); }
virtual ~TConnectionDelegate() { }
protected:
virtual bool open() { return m_connectionDelegate.fpOpen(m_connectionDelegate.data); }
SConnectionDelegate m_connectionDelegate;
};
} // namespace Socket
@@ -0,0 +1,28 @@
#pragma once
#include <ov_common_defines.h>
// Taken from
// - http://people.redhat.com/drepper/dsohowto.pdf
// - http://www.nedprod.com/programs/gccvisibility.html
#if defined Socket_Shared
# if defined TARGET_OS_Windows
# define Socket_API_Export __declspec(dllexport)
# define Socket_API_Import __declspec(dllimport)
# elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
# define Socket_API_Export __attribute__((visibility("default")))
# define Socket_API_Import __attribute__((visibility("default")))
# else
# define Socket_API_Export
# define Socket_API_Import
# endif
#else
# define Socket_API_Export
# define Socket_API_Import
#endif
#if defined Socket_Exports
# define Socket_API Socket_API_Export
#else
# define Socket_API Socket_API_Import
#endif
@@ -0,0 +1,194 @@
#pragma once
#include "IConnection.h"
#include <iostream>
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
// #include <netinet/in.h>
// #include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
// #include <netdb.h>
#include <ctime>
#if defined TARGET_OS_MacOS
#define Socket_SendFlags 0
#define Socket_ReceiveFlags 0
#else
#define Socket_SendFlags MSG_NOSIGNAL
#define Socket_ReceiveFlags MSG_NOSIGNAL
#endif
#elif defined TARGET_OS_Windows
#include <WinSock2.h>
#include <Windows.h>
#define Socket_SendFlags 0
#define Socket_ReceiveFlags 0
#else
#endif
namespace Socket {
static bool FD_ISSET_PROXY(const int fd, fd_set* set) { return FD_ISSET(fd, set) ? true : false; }
template <class T>
class TConnection : public T
{
public:
TConnection() : m_socket(-1)
{
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#elif defined TARGET_OS_Windows
const int versionHigh = 2;
const int versionLow = 0;
const WORD winsockVersion = MAKEWORD(versionHigh, versionLow);
WSADATA wsaData;
WSAStartup(winsockVersion, &wsaData);
#else
#endif
}
explicit TConnection(const int socket) : m_socket(socket)
{
#if defined TARGET_OS_Windows
const int versionHigh = 2;
const int versionLow = 0;
const WORD winsockVersion = MAKEWORD(versionHigh, versionLow);
WSADATA wsaData;
WSAStartup(winsockVersion, &wsaData);
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#else
#endif
}
virtual ~TConnection()
{
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#elif defined TARGET_OS_Windows
WSACleanup();
#else
#endif
}
protected:
virtual bool open()
{
if (isConnected()) { return false; }
m_socket = int(socket(AF_INET, SOCK_STREAM, 0));
if (m_socket == size_t(-1)) { return false; }
return true;
}
public:
virtual bool close()
{
if (!isConnected()) { return false; }
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
::shutdown(m_socket, SHUT_RDWR);
::close(m_socket);
#elif defined TARGET_OS_Windows
shutdown(m_socket, SD_BOTH);
closesocket(m_socket);
#else
#endif
m_socket = -1;
return true;
}
virtual bool isReadyToSend(const size_t timeOut = 0) const
{
if (!isConnected()) { return false; }
struct timeval timeVal;
timeVal.tv_sec = int(timeOut / 1000);
timeVal.tv_usec = int((timeOut - timeVal.tv_sec * 1000) * 1000);
fd_set writeFileDesc;
FD_ZERO(&writeFileDesc);
FD_SET(m_socket, &writeFileDesc);
if (select(int(m_socket + 1), nullptr, &writeFileDesc, nullptr, &timeVal) < 0) { return false; }
if (!FD_ISSET_PROXY(int(m_socket), &writeFileDesc)) { return false; }
return true;
}
virtual bool isReadyToReceive(const size_t timeOut = 0) const
{
if (!isConnected()) { return false; }
struct timeval timeVal;
timeVal.tv_sec = int(timeOut / 1000);
timeVal.tv_usec = int((timeOut - timeVal.tv_sec * 1000) * 1000);
fd_set readFileDesc;
FD_ZERO(&readFileDesc);
FD_SET(m_socket, &readFileDesc);
if (select(int(m_socket + 1), &readFileDesc, nullptr, nullptr, &timeVal) < 0) { return false; }
if (!(FD_ISSET_PROXY(int(m_socket), &readFileDesc))) { return false; }
return true;
}
virtual size_t sendBuffer(const void* buffer, const size_t size)
{
if (!isConnected()) { return 0; }
const int res = send(m_socket, static_cast<const char*>(buffer), int(size), Socket_SendFlags);
if (size != 0 && res <= 0) { close(); }
return res <= 0 ? 0 : size_t(res);
}
virtual size_t receiveBuffer(void* buffer, const size_t size)
{
if (!isConnected() || !size) { return 0; }
const int res = recv(m_socket, static_cast<char*>(buffer), int(size), Socket_ReceiveFlags);
if (size != 0 && res <= 0) { close(); }
return res <= 0 ? 0 : size_t(res);
}
virtual bool sendBufferBlocking(const void* buffer, const size_t size)
{
size_t leftBytes = size;
const char* tmpBuffer = static_cast<const char*>(buffer);
do
{
leftBytes -= sendBuffer(tmpBuffer + size - leftBytes, leftBytes);
if (!isConnected()) { return false; }
} while (leftBytes != 0);
return true;
}
virtual bool receiveBufferBlocking(void* buffer, const size_t size)
{
size_t leftBytes = size;
char* tmpBuffer = static_cast<char*>(buffer);
do
{
leftBytes -= receiveBuffer(tmpBuffer + size - leftBytes, leftBytes);
if (!isConnected()) { return false; }
} while (leftBytes != 0);
return true;
}
virtual bool isConnected() const { return m_socket != size_t(-1); }
virtual void release()
{
if (isConnected()) { close(); }
delete this;
}
protected:
size_t m_socket;
};
} // namespace Socket
@@ -0,0 +1,442 @@
#include "IConnectionBluetooth.h"
#if defined TARGET_OS_Windows
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#include <Windows.h>
#include <CommCtrl.h>
#include <codecvt>
#include <WinSock2.h>
#include <ws2bth.h>
#include <bluetoothapis.h>
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#else
#error "Unsupported platform"
#endif
#include <assert.h>
#include <vector>
#include <string>
namespace Socket {
class CConnectionBluetooth final : public IConnectionBluetooth
{
public:
#if defined TARGET_OS_Windows
CConnectionBluetooth() : m_Socket(INVALID_SOCKET) { }
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
CConnectionBluetooth() : m_LastError() { }
#endif
#if defined TARGET_OS_Windows
bool initialize()
{
WSADATA wsaData;
// Ask for Winsock version.
if (_WINSOCK2API_::WSAStartup(MAKEWORD(WIN_SOCKET_MAJOR_VERSION, WIN_SOCKET_MINOR_VERSION), &wsaData) != 0)
{
m_LastError = "Failed to start Winsock " + std::to_string(WIN_SOCKET_MAJOR_VERSION) + "." + std::to_string(WIN_SOCKET_MINOR_VERSION) + ": " + this->
getLastErrorFormated();
return false;
}
// Confirm that the WinSock DLL supports version requested.
// Note that if the DLL supports versions greater than the version requested, in addition to the version requested, it will still return the version requested in wVersion.
if (LOBYTE(wsaData.wVersion) != WIN_SOCKET_MAJOR_VERSION || HIBYTE(wsaData.wVersion) != WIN_SOCKET_MINOR_VERSION)
{
m_LastError = "Could not find a usable version of Winsock.dll.";
_WINSOCK2API_::WSACleanup();
return false;
}
return true;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
bool initialize() { return false; }
#endif
bool open() override { return false; }
bool close() override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device is not connected.";
return false;
}
#if defined TARGET_OS_Windows
bool isSuccess = true;
if (m_Socket != INVALID_SOCKET)
{
// shutdown the connection since no more data will be sent or received
if (_WINSOCK2API_::shutdown(m_Socket, SD_BOTH) == SOCKET_ERROR)
{
m_LastError = "Failed to shutdown the bluetooth socket:" + this->getLastErrorFormated();
isSuccess = false;
}
if (_WINSOCK2API_::closesocket(m_Socket) == SOCKET_ERROR)
{
m_LastError = "Failed to close the bluetooth socket:" + this->getLastErrorFormated();
isSuccess = false;
}
if (_WINSOCK2API_::WSACleanup() == SOCKET_ERROR)
{
m_LastError = "Failed to cleanup the bluetooth socket:" + this->getLastErrorFormated();
isSuccess = false;
}
m_Socket = INVALID_SOCKET;
}
return isSuccess;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
#endif
}
bool isReadyToSend(const size_t /*timeOut*/) const override { return this->isConnected(); }
bool isReadyToReceive(const size_t /*timeOut*/) const override
{
if (!this->isConnected()) { return false; }
#if defined TARGET_OS_Windows
unsigned long nPendingBytes = 0;
if (_WINSOCK2API_::ioctlsocket(m_Socket, FIONREAD, &nPendingBytes) == SOCKET_ERROR)
{
//m_LastError = "Failed to get the pending bytes count: " + this->getLastErrorFormated();
return false;
}
return nPendingBytes != 0;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
#endif
}
size_t getPendingByteCount() override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device not connected.";
return 0;
}
#if defined TARGET_OS_Windows
unsigned long nPendingBytes = 0;
if (_WINSOCK2API_::ioctlsocket(m_Socket, FIONREAD, &nPendingBytes) == SOCKET_ERROR)
{
m_LastError = "Failed to get the pending bytes count: " + this->getLastErrorFormated();
return 0;
}
return nPendingBytes;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return 0;
#endif
}
size_t sendBuffer(const void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device is not connected.";
return 0;
}
#if defined TARGET_OS_Windows
const int nBytesSent = _WINSOCK2API_::send(m_Socket, reinterpret_cast<const char*>(buffer), int(size), 0);
if (nBytesSent == SOCKET_ERROR)
{
m_LastError = "Failed to write on the bluetooth port: " + getLastErrorFormated();
this->close();
return 0;
}
return size_t(nBytesSent);
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return 0;
#endif
}
size_t receiveBuffer(void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device is not connected.";
return 0;
}
#if defined TARGET_OS_Windows
const int nBytesReceived = _WINSOCK2API_::recv(m_Socket, static_cast<char*>(buffer), int(size), 0);
if (nBytesReceived == SOCKET_ERROR)
{
m_LastError = "Failed to receive data from bluetooth: " + getLastErrorFormated();
this->close();
return 0;
}
return size_t(nBytesReceived);
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return 0;
#endif
}
bool sendBufferBlocking(const void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device is not connected.";
return false;
}
const char* p = reinterpret_cast<const char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected())
{
bytesLeft -= this->sendBuffer(p + size - bytesLeft, bytesLeft);
if (this->isErrorRaised()) { return false; }
}
return bytesLeft == 0;
}
bool receiveBufferBlocking(void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Bluetooth device is not connected.";
return false;
}
char* p = reinterpret_cast<char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected())
{
bytesLeft -= this->receiveBuffer(p + size - bytesLeft, bytesLeft);
if (this->isErrorRaised()) { return false; }
}
return bytesLeft == 0;
}
bool isConnected() const override
{
#if defined TARGET_OS_Windows
return m_Socket != INVALID_SOCKET;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
#endif
}
void release() override { delete this; }
bool connect(const uint64_t u64BluetoothAddress) override
{
m_LastError.clear();
if (this->isConnected())
{
m_LastError = "Bluetooth device is already connected";
return false;
}
#if defined TARGET_OS_Windows
if (!this->initialize()) { return false; }
m_Socket = _WINSOCK2API_::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if (m_Socket == INVALID_SOCKET)
{
m_LastError = "Failed to create bluetooth socket: " + getLastErrorFormated();
_WINSOCK2API_::WSACleanup();
return false;
}
SOCKADDR_BTH sockAddressBlutoothServer;
sockAddressBlutoothServer.btAddr = u64BluetoothAddress;
sockAddressBlutoothServer.addressFamily = AF_BTH;
sockAddressBlutoothServer.serviceClassId = RFCOMM_PROTOCOL_UUID;
sockAddressBlutoothServer.port = BT_PORT_ANY;
if (_WINSOCK2API_::connect(m_Socket, reinterpret_cast<SOCKADDR*>(&sockAddressBlutoothServer), sizeof(SOCKADDR_BTH)) == SOCKET_ERROR)
{
m_LastError = "Failed to connect the socket to the bluetooth address [" + std::to_string(sockAddressBlutoothServer.btAddr) + "]: " +
getLastErrorFormated();
_WINSOCK2API_::closesocket(m_Socket); // Returned code not checked.
_WINSOCK2API_::WSACleanup(); // Returned code not checked.
m_Socket = INVALID_SOCKET;
return false;
}
return true;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
#endif
}
bool isErrorRaised() const { return !m_LastError.empty(); }
const char* getLastError() const override { return m_LastError.c_str(); }
static std::string getLastErrorFormated()
{
#if defined TARGET_OS_Windows
LPTSTR text;
const DWORD errCode = GetLastError();
const size_t size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | // use system message tables to retrieve error text
FORMAT_MESSAGE_ALLOCATE_BUFFER | // allocate buffer on local heap for error text
FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters
nullptr, // unused with FORMAT_MESSAGE_FROM_SYSTEM
errCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
LPTSTR(&text), // output
0, // minimum size for output buffer
nullptr);
// Converts std::wstring to std::string and returns it.
const std::wstring message(text, size);
LocalFree(text);
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
return converter.to_bytes(message);
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return "";
#endif
}
void clearError() override { m_LastError.clear(); }
bool listPairedBluetoothDevices(size_t* nPairedBluetoothDevices, char** names, uint64_t** addresses) override
{
std::vector<std::string> devicesNames;
std::vector<uint64_t> devicesAddresses;
#if defined TARGET_OS_Windows
HANDLE handle;
WSAQUERYSET wsaQuerySet;
memset((void*)&wsaQuerySet, 0, sizeof(wsaQuerySet));
wsaQuerySet.dwSize = sizeof(wsaQuerySet);
wsaQuerySet.dwNameSpace = NS_BTH;
wsaQuerySet.lpcsaBuffer = nullptr;
if (_WINSOCK2API_::WSALookupServiceBegin(&wsaQuerySet, LUP_CONTAINERS | LUP_RETURN_NAME | LUP_RETURN_ADDR, &handle) == SOCKET_ERROR)
{
m_LastError = "Failed to start the Bluetooth lookup service: " + getLastErrorFormated();
return false;
}
char buffer[5000];
const LPWSAQUERYSET wsaQuerySetW = LPWSAQUERYSET(buffer);
DWORD size = sizeof(buffer);
memset((void*)wsaQuerySetW, 0, sizeof(WSAQUERYSET));
wsaQuerySetW->dwSize = sizeof(WSAQUERYSET);
wsaQuerySetW->dwNameSpace = NS_BTH;
wsaQuerySetW->lpBlob = nullptr;
bool lookup = true;
while (lookup)
{
// Check next bluetooth device
const int res = _WINSOCK2API_::WSALookupServiceNext(handle, LUP_RETURN_NAME | LUP_RETURN_ADDR, &size, wsaQuerySetW);
if (res == SOCKET_ERROR)
{
// If it is a "real" error, we trace it and return false.
if (_WINSOCK2API_::WSAGetLastError() != WSA_E_NO_MORE)
{
m_LastError = "Lookup service next operation failed: " + getLastErrorFormated();
return false;
}
// Else, it is because there is no more Bluetooth devices available.
lookup = false;
break;
}
// Get bluetooth MAC address and name
devicesAddresses.push_back(reinterpret_cast<SOCKADDR_BTH*>(wsaQuerySetW->lpcsaBuffer->RemoteAddr.lpSockaddr)->btAddr);
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converterX;
devicesNames.push_back(converterX.to_bytes(wsaQuerySetW->lpszServiceInstanceName));
}
if (_WINSOCK2API_::WSALookupServiceEnd(handle) == SOCKET_ERROR)
{
m_LastError = "Failed to stop the Bluetooth lookup service: " + getLastErrorFormated();
return false;
}
*nPairedBluetoothDevices = devicesAddresses.size();
names = new char*[*nPairedBluetoothDevices];
for (size_t i = 0; i < *nPairedBluetoothDevices; ++i)
{
names[i] = new char[devicesNames[i].size() + 1];
std::strcpy(names[i], devicesNames[i].c_str());
}
addresses = new uint64_t*[*nPairedBluetoothDevices];
return true;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
#endif
}
std::string m_LastError;
#if defined TARGET_OS_Windows
SOCKET m_Socket;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#endif
};
IConnectionBluetooth* createConnectionBluetooth() { return new CConnectionBluetooth(); }
} // namespace Socket
@@ -0,0 +1,171 @@
#include "IConnection.h"
#include "IConnectionClient.h"
#include "IConnection.inl"
#include <cstring>
#include <iostream>
#include <fcntl.h>
#include <cerrno>
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <unistd.h>
#elif defined TARGET_OS_Windows
#include <cerrno>
#include <WS2tcpip.h>
#else
#endif
namespace Socket {
class CConnectionClient final : public TConnection<IConnectionClient>
{
public:
bool connect(const char* sServerName, const size_t serverPort, const size_t timeOut) override
{
if (!open()) { return false; }
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
long value;
// Sets non blocking
if((value = ::fcntl(m_socket, F_GETFL, nullptr)) < 0)
{
close();
return false;
}
value|=O_NONBLOCK;
if(::fcntl(m_socket, F_SETFL, value)<0)
{
close();
return false;
}
// Looks up host name
struct hostent* serverHostEntry = gethostbyname(sServerName);
if(!serverHostEntry)
{
close();
return false;
}
#elif defined TARGET_OS_Windows
// Sets non blocking
unsigned long mode = 1;
ioctlsocket(m_socket, FIONBIO, &mode);
struct addrinfo hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
PADDRINFOA addr;
if (getaddrinfo(sServerName, nullptr, &hints, &addr) != 0)
{
close();
return false;
}
const auto sockaddrIPV4 = reinterpret_cast<sockaddr_in*>(addr->ai_addr);
#endif
// Connects
struct sockaddr_in serverAddress;
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(static_cast<unsigned short>(serverPort));
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
serverAddress.sin_addr=*((struct in_addr*)serverHostEntry->h_addr);
#elif defined TARGET_OS_Windows
serverAddress.sin_addr = sockaddrIPV4->sin_addr;
freeaddrinfo(addr);
#endif
errno = 0;
if (::connect(m_socket, reinterpret_cast<struct sockaddr*>(&serverAddress), sizeof(struct sockaddr_in)) < 0)
{
bool inProgress;
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
inProgress = (errno==EINPROGRESS);
#elif defined TARGET_OS_Windows
inProgress = (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK);
#else
inProgress = false;
#endif
if (inProgress)
{
const int t = (timeOut == 0xffffffff) ? 125 : int(timeOut);
// Performs time out
struct timeval timeVal;
timeVal.tv_sec = (t / 1000);
timeVal.tv_usec = ((t - timeVal.tv_sec * 1000) * 1000);
fd_set writeFileDesc;
FD_ZERO(&writeFileDesc);
FD_SET(m_socket, &writeFileDesc);
if (select(int(m_socket + 1), nullptr, &writeFileDesc, nullptr, &timeVal) < 0)
{
close();
return false;
}
if (!FD_ISSET_PROXY(int(m_socket), &writeFileDesc))
{
close();
return false;
}
// Checks error status
int option = 0;
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
socklen_t length = sizeof(option);
::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, (void*)(&option), &length);
#elif defined TARGET_OS_Windows
int length = sizeof(option);
getsockopt(m_socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&option), &length);
#endif
if (option != 0)
{
close();
return false;
}
}
else
{
close();
return false;
}
}
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
// Sets back to blocking
if((value=::fcntl(m_socket, F_GETFL, nullptr))<0)
{
close();
return false;
}
value&=~O_NONBLOCK;
if(::fcntl(m_socket, F_SETFL, value)<0)
{
close();
return false;
}
#elif defined TARGET_OS_Windows
// Sets back to blocking
mode = 0;
ioctlsocket(m_socket, FIONBIO, &mode);
#endif
return true;
}
};
IConnectionClient* createConnectionClient() { return new CConnectionClient(); }
} // namespace Socket
@@ -0,0 +1,261 @@
#include "IConnectionParallel.h"
#if defined TARGET_OS_Windows
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commctrl.h>
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <sys/ioctl.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/errno.h>
#else
#error "Unsupported platform"
#endif
#if defined TARGET_OS_Linux && !defined TARGET_OS_Android
#include <linux/ppdev.h>
#include <linux/parport.h>
#endif
#include <assert.h>
namespace Socket {
class CConnectionParallel final : public IConnectionParallel
{
protected:
unsigned short m_portNumber;
std::string m_lastError;
public:
CConnectionParallel()
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
:m_file(0)
#endif
{
#if defined TARGET_OS_Windows
m_portNumber = 0;
m_port = LoadLibrary(TEXT("TVicPort.dll"));
if (m_port != nullptr)
{
m_isDriverOpened = port_is_driver_opened_t(GetProcAddress(m_port, "IsDriverOpened"));
m_portOpen = port_open_t(GetProcAddress(m_port, "OpenTVicPort"));
m_portClose = port_close_t(GetProcAddress(m_port, "CloseTVicPort"));
m_portWrite = port_write_t(GetProcAddress(m_port, "WritePort"));
if (!m_isDriverOpened || !m_portOpen || !m_portClose || !m_portWrite)
{
m_lastError = "Cannot load function from TVicPort.dll: " + this->getLastErrorFormated();
this->release();
}
}
else { m_lastError = "Cannot found or open TVicPort.dll: " + this->getLastErrorFormated(); }
#endif
}
bool open() override
{
// Should never be used
return false;
}
bool close() override
{
#if defined TARGET_OS_Windows
if (m_port != nullptr)
{
if (m_isDriverOpened())
{
m_portNumber = 0;
m_portClose();
return true;
}
m_lastError = "Cannot close the TVicPort library because it is not opened.";
return false;
}
return false;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
/*if (ioctl(fd, PPRELEASE) < 0) { return false; }
if (close(m_file) < 0) { return false; }
else
{
m_file = -1;
}*/
#endif
}
bool isReadyToSend(const size_t /*timeOut*/ = 0) const override { return this->isConnected(); }
bool isReadyToReceive(const size_t /*timeOut*/ = 0) const override { return this->isConnected(); }
size_t getPendingByteCount() const { return (this->isConnected() ? 0 : 1); }
size_t sendBuffer(const void* buffer, const size_t size = 8) override
{
if (!this->isConnected()) { return 0; }
#if defined TARGET_OS_Windows
const uint8_t value = *(static_cast<const uint8_t*>(buffer));
m_portWrite(m_portNumber, value);
return size;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return 0;
/*if (ioctl(m_file, PPWDATA, &value) < 0) { return size; }*/
#endif
}
size_t receiveBuffer(void* /*buffer*/, const size_t /*size*/ = 8) override
{
if (!this->isConnected()) { return 0; }
#if defined TARGET_OS_Windows
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
/*
unsigned char valin;
int dirin=1;
int dirout=0;
if (ioctl (fd, PPDATADIR, &dirin) < 0) { return 0; }
if (ioctl(fd, PPRDATA, &valin) < 0) { return 0; }
int res = ::read(m_file, buffer, size);
if(res < 0)
{
this->close();
return 0;
}
return res;
*/
#endif
return 0;
}
bool sendBufferBlocking(const void* buffer, const size_t size) override
{
const char* p = reinterpret_cast<const char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected()) { bytesLeft -= this->sendBuffer(p + size - bytesLeft, bytesLeft); }
return this->isConnected();
}
bool receiveBufferBlocking(void* buffer, const size_t size) override
{
char* p = reinterpret_cast<char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected()) { bytesLeft -= this->receiveBuffer(p + size - bytesLeft, bytesLeft); }
return this->isConnected();
}
#if defined TARGET_OS_Windows
bool isConnected() const override { return m_portNumber != 0; }
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
bool isConnected() const override { return m_file != 0; }
#endif
void release() override
{
#if defined TARGET_OS_Windows
if (m_port != nullptr && !FreeLibrary(m_port)) { m_lastError = getLastErrorFormated(); }
#endif
delete this;
}
bool connect(const unsigned short portNumber) override
{
if (this->isConnected()) { return false; }
#if defined TARGET_OS_Windows
if (m_port != nullptr)
{
if (m_portOpen())
{
m_lastError = "No error";
m_portNumber = portNumber;
return true;
}
m_lastError = "Cannot open the TVic library";
m_portNumber = 0;
return false;
}
m_lastError = "TVicPort library is not loaded.";
return false;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return false;
/*
std::string url = "/dev/parport" + std::to_string(portNumber);
if ((m_file = open(url.c_str() , O_RDWR)) < 0)
{
this->close();
return false;
}
else
{
if (ioctl(m_file, PPCLAIM) < 0)
{
this->close();
return false;
}
}
*/
#endif
}
std::string getLastError() override { return m_lastError; }
std::string getLastErrorFormated()
{
#if defined TARGET_OS_Windows
LPTSTR errorText;
const DWORD error = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM // use system message tables to retrieve error text
| FORMAT_MESSAGE_ALLOCATE_BUFFER // allocate buffer on local heap for error text
| FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters
nullptr, // unused with FORMAT_MESSAGE_FROM_SYSTEM
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
LPTSTR(&errorText), // output
0, // minimum size for output buffer
nullptr); // arguments - see note
return errorText + std::to_string(static_cast<uint64_t>(error));
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return "Not implemented.";
#endif
}
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
int m_file;
#endif
};
IConnectionParallel* createConnectionParallel() { return new CConnectionParallel(); }
} // namespace Socket
@@ -0,0 +1,475 @@
#include "IConnectionSerial.h"
#if defined TARGET_OS_Windows
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commctrl.h>
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#else
#error "Unsupported platform"
#endif
#include <assert.h>
#include <iostream>
#if defined TARGET_OS_Linux
// Good resources at http://www.tldp.org/HOWTO/html_single/Serial-HOWTO/
#endif
namespace Socket {
class CConnectionSerial final : public IConnectionSerial
{
public:
#if defined TARGET_OS_Windows
CConnectionSerial() : m_file(nullptr) { }
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
CConnectionSerial() : m_file(0) { }
#endif
bool open() override { return false; } // Should never be used
bool close() override
{
#if defined TARGET_OS_Windows
if (m_file != nullptr)
{
if (!CloseHandle(m_file))
{
m_LastError = "Failed to close the serial port:" + this->getLastErrorFormated();
m_file = nullptr;
return false;
}
m_file = nullptr;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
if(m_file != -1)
{
::close(m_file);
m_file=-1;
}
#endif
return true;
}
#if defined TARGET_OS_Windows
bool isReadyToSend(const size_t /*timeOut*/) const override
{
if (!this->isConnected()) { return false; }
return true;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
bool isReadyToSend(const size_t timeOut) const override
{
if (!this->isConnected()) { return false; }
fd_set fileDescSet;
struct timeval time;
time.tv_sec = timeOut/1000;
time.tv_usec = (timeOut%1000)*1000;
FD_ZERO(&fileDescSet);
FD_SET(m_file, &fileDescSet);
if(!::select(m_file + 1, nullptr, &fileDescSet, nullptr, &time)) { return false; }
if(FD_ISSET(m_file, &fileDescSet)) { return true; }
return false;
}
#endif
#if defined TARGET_OS_Windows
bool isReadyToReceive(const size_t /*timeOut*/) const override
{
if (!this->isConnected()) { return false; }
struct _COMSTAT status;
DWORD state;
if (ClearCommError(m_file, &state, &status) != 0) { return status.cbInQue != 0; }
return false;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
bool isReadyToReceive(const size_t timeOut) const override
{
if (!this->isConnected()) { return false; }
fd_set fileDescSet;
struct timeval time;
time.tv_sec=timeOut/1000;
time.tv_usec=(timeOut%1000)*1000;
FD_ZERO(&fileDescSet);
FD_SET(m_file, &fileDescSet);
if(!::select(m_file+1, &fileDescSet, nullptr, nullptr, &time)) { return false; }
if(FD_ISSET(m_file, &fileDescSet)) { return true; }
return false;
}
#endif
size_t getPendingByteCount() override
{
if (!this->isConnected())
{
m_LastError = "Serial port not connected.";
return 0;
}
#if defined TARGET_OS_Windows
struct _COMSTAT status;
DWORD state;
if (ClearCommError(m_file, &state, &status) == 0)
{
m_LastError = "Failed to clear the serial port communication error: " + this->getLastErrorFormated();
return 0;
}
return status.cbInQue;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
int byteCount=0;
if(-1 == ::ioctl(m_file, FIONREAD, &byteCount))
{
m_LastError = "Failed to querry pending bytes in ioctl";
return 0;
}
return byteCount;
#endif
}
bool flush() override
{
if (!this->isConnected())
{
m_LastError = "Serial port not connected.";
return false;
}
#if defined TARGET_OS_Windows
if (!FlushFileBuffers(m_file))
{
m_LastError = "Failed to flush serial port buffer: " + this->getLastErrorFormated();
return false;
}
return true;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
if(-1 == ::tcdrain(m_file))
{
m_LastError = "Could not flush connection in tcdrain";
return false;
}
return true;
#else
return false;
#endif
}
size_t sendBuffer(const void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Serial port not connected.";
return 0;
}
#if defined TARGET_OS_Windows
DWORD written = 0;
if (!WriteFile(m_file, buffer, DWORD(size), &written, nullptr))
{
m_LastError = "Failed to write on serial port: " + this->getLastErrorFormated();
this->close();
return 0;
}
if (written == 0)
{
m_LastError = "Serial port timeout when trying to write.";
this->close();
return 0;
}
return written;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
int res = ::write(m_file, buffer, size);
if(res < 0)
{
m_LastError = "Could not write on connection";
this->close();
return 0;
}
return res;
#else
return 0;
#endif
}
size_t receiveBuffer(void* buffer, const size_t size) override
{
if (!this->isConnected())
{
m_LastError = "Serial port not connected.";
return 0;
}
#if defined TARGET_OS_Windows
DWORD read = 0;
if (!ReadFile(m_file, buffer, DWORD(size), &read, nullptr))
{
m_LastError = "Failed to read on serial port: " + this->getLastErrorFormated();
this->close();
return 0;
}
if (read == 0)
{
m_LastError = "Serial port timeout when trying to read.";
this->close();
return 0;
}
return read;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
int res = ::read(m_file, buffer, size);
if (res < 0)
{
m_LastError = "Could not read from connection";
this->close();
return 0;
}
return res;
#else
return 0;
#endif
}
bool sendBufferBlocking(const void* buffer, const size_t size) override
{
const char* p = reinterpret_cast<const char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected())
{
bytesLeft -= this->sendBuffer(p + size - bytesLeft, bytesLeft);
if (this->isErrorRaised()) { return false; }
}
return bytesLeft == 0;
}
bool receiveBufferBlocking(void* buffer, const size_t size) override
{
char* p = reinterpret_cast<char*>(buffer);
size_t bytesLeft = size;
while (bytesLeft != 0 && this->isConnected())
{
bytesLeft -= this->receiveBuffer(p + size - bytesLeft, bytesLeft);
if (this->isErrorRaised()) { return false; }
}
return bytesLeft == 0;
}
bool isConnected() const override
{
#if defined TARGET_OS_Windows
return ((m_file != nullptr) && (m_file != INVALID_HANDLE_VALUE));
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return m_file != 0;
#endif
}
void release() override { delete this; }
bool connect(const char* sURL, const size_t baudrate) override
{
m_LastError.clear();
if (this->isConnected())
{
m_LastError = "Serial port already connected";
return false;
}
#if defined TARGET_OS_Windows
m_file = ::CreateFile(sURL, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (m_file == INVALID_HANDLE_VALUE || m_file == nullptr)
{
m_LastError = "Failed to open serial port: " + this->getLastErrorFormated();
m_file = nullptr;
return false;
}
DCB dcb = { 0 };
if (!GetCommState(m_file, &dcb))
{
m_LastError = "Failed to get communication state: " + this->getLastErrorFormated();
this->close();
return false;
}
dcb.DCBlength = sizeof(dcb);
dcb.BaudRate = DWORD(baudrate);
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
ClearCommError(m_file, nullptr, nullptr);
if (!SetCommState(m_file, &dcb))
{
m_LastError = "Could not set communication state: " + this->getLastErrorFormated();
this->close();
return false;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
/* $$$ BAUD RATE PARAMETER IS NOT CONSIDERED ON LINUX, MIGHT BE NECESSARY TO CHANGE THIS $$$ */
assert(baudrate == 230400);
if ((m_file = ::open(sURL, O_RDWR)) == -1)
{
m_LastError = "Failed to open serial port.";
return false;
}
struct termios terminalAtt;
if(::tcgetattr(m_file, &terminalAtt)!=0)
{
m_LastError = "Could not get terminal attributes in tcgetattr";
this->close();
return false;
}
terminalAtt.c_cflag = B230400 | CS8 | CLOCAL | CREAD;
terminalAtt.c_iflag = 0;
terminalAtt.c_oflag = OPOST | ONLCR;
terminalAtt.c_lflag = 0;
if (::tcsetattr(m_file, TCSAFLUSH, &terminalAtt) != 0)
{
m_LastError = "Could not set terminal attributes in tcgetattr";
this->close();
return false;
}
#endif
return true;
}
bool setTimeouts(const size_t decisecondsTimeout) override
{
if (!this->isConnected()) { return false; }
#if defined TARGET_OS_Windows
COMMTIMEOUTS timeouts;
if (!GetCommTimeouts(m_file, &timeouts))
{
m_LastError = "Could not get communication timeouts: " + this->getLastErrorFormated();
this->close();
return false;
}
timeouts.ReadTotalTimeoutConstant = decisecondsTimeout * 100; // Deciseconds to milliseconds
timeouts.WriteTotalTimeoutConstant = decisecondsTimeout * 100; // Deciseconds to milliseconds
if (!SetCommTimeouts(m_file, &timeouts))
{
m_LastError = "Could not set communication timeouts: " + this->getLastErrorFormated();
this->close();
return false;
}
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
struct termios terminalAtt;
if(::tcgetattr(m_file, &terminalAtt)!=0)
{
m_LastError = "Could not get terminal attributes in tcgetattr";
this->close();
return false;
}
terminalAtt.c_cc[VTIME] = decisecondsTimeout;
if (::tcsetattr(m_file, TCSAFLUSH, &terminalAtt) != 0)
{
m_LastError = "Could not set terminal attributes in tcgetattr";
this->close();
return false;
}
#endif
return true;
}
const char* getLastError() override { return m_LastError.c_str(); }
static std::string getLastErrorFormated()
{
#if defined TARGET_OS_Windows
LPTSTR errorText;
const DWORD error = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | // use system message tables to retrieve error text
FORMAT_MESSAGE_ALLOCATE_BUFFER | // allocate buffer on local heap for error text
FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters
nullptr, // unused with FORMAT_MESSAGE_FROM_SYSTEM
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
LPTSTR(&errorText), // output
0, // minimum size for output buffer
nullptr); // arguments - see note
return errorText;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return ""; // TODO
#endif
}
bool isErrorRaised() override { return !m_LastError.empty(); }
void clearError() override { m_LastError.clear(); }
std::string m_LastError;
#if defined TARGET_OS_Windows
void* m_file;
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
int m_file;
#endif
};
IConnectionSerial* createConnectionSerial() { return new CConnectionSerial(); }
} // namespace Socket
@@ -0,0 +1,36 @@
#include "IConnectionSerialDelegate.h"
namespace Socket {
class CConnectionSerialDelegate final : public IConnectionSerialDelegate
{
public:
explicit CConnectionSerialDelegate(const SConnectionSerialDelegate connectionSerialDelegate) : IConnectionSerialDelegate(connectionSerialDelegate)
{
m_connectionSerialDelegate = connectionSerialDelegate;
}
~CConnectionSerialDelegate() override { }
bool connect(const char* url, const size_t baudRate) override
{
return m_connectionSerialDelegate.fpConnect(m_connectionSerialDelegate.connectionDelegate.data, url, baudRate);
}
size_t getPendingByteCount() override { return m_connectionSerialDelegate.fpGetPendingByteCount(m_connectionSerialDelegate.connectionDelegate.data); }
bool flush() override { return m_connectionSerialDelegate.fpFlush(m_connectionSerialDelegate.connectionDelegate.data); }
const char* getLastError() override { return m_connectionSerialDelegate.fpGetLastError(m_connectionSerialDelegate.connectionDelegate.data); }
bool isErrorRaised()
override { return false; } // return m_connectionSerialDelegate.fpIsErrorRaised(m_connectionSerialDelegate.connectionDelegate.data);
void clearError() override { } // return m_connectionSerialDelegate.fpClearError(m_connectionSerialDelegate.connectionDelegate.data);
bool setTimeouts(const size_t /*timeout*/)
override { return true; } // return m_connectionSerialDelegate.fpSetTimeouts(m_connectionSerialDelegate.connectionDelegate.data, decisecondsTimeout);
};
IConnectionSerialDelegate* createConnectionSerialDelegate(const SConnectionSerialDelegate connectionSerialDelegate)
{
return new CConnectionSerialDelegate(connectionSerialDelegate);
}
} // namespace Socket
@@ -0,0 +1,112 @@
#include "IConnection.h"
#include "IConnectionServer.h"
#include "IConnection.inl"
#include <cstring>
#include <iostream>
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <cerrno>
#elif defined TARGET_OS_Windows
#else
#endif
namespace Socket {
class CConnectionServer final : public TConnection<IConnectionServer>
{
public:
bool listen(const size_t port) override
{
if (!open()) { return false; }
int reuseAddress = 1;
#if defined TARGET_OS_Windows
setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&reuseAddress), sizeof(reuseAddress));
#else
::setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, &reuseAddress, sizeof(reuseAddress));
#endif
struct sockaddr_in localHostAddress;
memset(&localHostAddress, 0, sizeof(localHostAddress));
localHostAddress.sin_family = AF_INET;
localHostAddress.sin_port = htons(static_cast<unsigned short>(port));
localHostAddress.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(m_socket, reinterpret_cast<struct sockaddr*>(&localHostAddress), sizeof(localHostAddress)) == -1)
{
/*
switch(errno)
{
case EBADF: std::cout << "EBADF" << std::endl; break;
case ENOTSOCK: std::cout << "ENOTSOCK" << std::endl; break;
case EADDRINUSE: std::cout << "EADDRINUSE" << std::endl; break;
case EINVAL: std::cout << "EINVAL" << std::endl; break;
case EROFS: std::cout << "EROFS" << std::endl; break;
case EFAULT: std::cout << "EFAULT" << std::endl; break;
case ENAMETOOLONG: std::cout << "ENAMETOOLONG" << std::endl; break;
case ENOENT: std::cout << "ENOENT" << std::endl; break;
case ENOMEM: std::cout << "ENOMEM" << std::endl; break;
case ENOTDIR: std::cout << "ENOTDIR" << std::endl; break;
case EACCES: std::cout << "EACCES" << std::endl; break;
case ELOOP: std::cout << "ELOOP" << std::endl; break;
default: std::cout << "Bind_unknown" << std::endl;
}
*/
return false;
}
if (::listen(m_socket, 32) == -1)
{
/*
switch(errno)
{
case EADDRINUSE: std::cout << "EADDRINUSE" << std::endl; break;
case EBADF: std::cout << "EBADF" << std::endl; break;
case ENOTSOCK: std::cout << "ENOTSOCK" << std::endl; break;
case EOPNOTSUPP: std::cout << "EOPNOTSUPP" << std::endl; break;
default: std::cout << "Listen_unknown" << std::endl;
}
*/
return false;
}
return true;
}
IConnection* accept() override
{
struct sockaddr_in clientAddress;
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
socklen_t clientAddressSize = sizeof(clientAddress);
#elif defined TARGET_OS_Windows
int clientAddressSize = sizeof(clientAddress);
#else
#endif
const int clientSocket = int(::accept(m_socket, reinterpret_cast<struct sockaddr*>(&clientAddress), &clientAddressSize));
if (clientSocket == -1) { return nullptr; }
return new TConnection<IConnection>(int(clientSocket));
}
bool getSocketPort(size_t& port) override
{
struct sockaddr_in socketInfo;
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
socklen_t socketInfoLength = sizeof(socketInfo);
#elif defined TARGET_OS_Windows
int socketInfoLength = sizeof(socketInfo);
#endif
if (getsockname(m_socket, reinterpret_cast<sockaddr*>(&socketInfo), &socketInfoLength) == -1) { return false; }
port = size_t(ntohs(socketInfo.sin_port));
return true;
}
};
IConnectionServer* createConnectionServer() { return new CConnectionServer(); }
} // namespace Socket