init
This commit is contained in:
@@ -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
|
||||
+34
@@ -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
|
||||
Reference in New Issue
Block a user