254 lines
8.0 KiB
C++
254 lines
8.0 KiB
C++
//======================================================================================================
|
|
// Copyright 2025, Rokoko Glove OptiTrack Integration
|
|
//======================================================================================================
|
|
|
|
#include "RokokoUDPReceiver.h"
|
|
#include <chrono>
|
|
#include <sstream>
|
|
#include <iostream>
|
|
|
|
namespace RokokoIntegration
|
|
{
|
|
RokokoUDPReceiver::RokokoUDPReceiver()
|
|
: mSocket(INVALID_SOCKET)
|
|
, mIsRunning(false)
|
|
, mIsListening(false)
|
|
, mPacketsReceived(0)
|
|
, mBytesReceived(0)
|
|
, mLastPacketTime(0.0)
|
|
, mPort(14043)
|
|
, mBufferSize(65000)
|
|
{
|
|
}
|
|
|
|
RokokoUDPReceiver::~RokokoUDPReceiver()
|
|
{
|
|
StopListening();
|
|
CloseSocket();
|
|
}
|
|
|
|
bool RokokoUDPReceiver::Initialize(int port)
|
|
{
|
|
try {
|
|
mPort = port;
|
|
|
|
// Winsock 초기화
|
|
WSADATA wsaData;
|
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
|
SetError("WSAStartup failed");
|
|
return false;
|
|
}
|
|
|
|
// UDP 소켓 생성
|
|
mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (mSocket == INVALID_SOCKET) {
|
|
SetError("Failed to create UDP socket");
|
|
WSACleanup();
|
|
return false;
|
|
}
|
|
|
|
// 소켓 옵션 설정
|
|
int sendBufferSize = mBufferSize;
|
|
if (setsockopt(mSocket, SOL_SOCKET, SO_SNDBUF,
|
|
(char*)&sendBufferSize, sizeof(sendBufferSize)) == SOCKET_ERROR) {
|
|
SetError("Failed to set send buffer size");
|
|
CloseSocket();
|
|
return false;
|
|
}
|
|
|
|
// 주소 구조체 설정
|
|
struct sockaddr_in serverAddr;
|
|
serverAddr.sin_family = AF_INET;
|
|
serverAddr.sin_port = htons(mPort);
|
|
serverAddr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
// 소켓 바인딩
|
|
if (bind(mSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
|
|
std::ostringstream oss;
|
|
oss << "Failed to bind to port " << mPort << ". Port may be in use.";
|
|
SetError(oss.str());
|
|
CloseSocket();
|
|
return false;
|
|
}
|
|
|
|
// 논블로킹 모드 설정
|
|
u_long mode = 1;
|
|
if (ioctlsocket(mSocket, FIONBIO, &mode) == SOCKET_ERROR) {
|
|
SetError("Failed to set non-blocking mode");
|
|
CloseSocket();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
} catch (...) {
|
|
SetError("Exception during UDP initialization");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool RokokoUDPReceiver::StartListening()
|
|
{
|
|
if (mSocket == INVALID_SOCKET) {
|
|
SetError("UDP socket not initialized");
|
|
return false;
|
|
}
|
|
|
|
if (mIsListening) {
|
|
SetError("Already listening");
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
mIsRunning = true;
|
|
mIsListening = true;
|
|
|
|
// 수신 스레드 시작
|
|
mReceiveThread = std::thread(&RokokoUDPReceiver::ReceiveThread, this);
|
|
|
|
return true;
|
|
|
|
} catch (...) {
|
|
SetError("Exception starting receive thread");
|
|
mIsRunning = false;
|
|
mIsListening = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void RokokoUDPReceiver::StopListening()
|
|
{
|
|
mIsRunning = false;
|
|
mIsListening = false;
|
|
|
|
if (mReceiveThread.joinable()) {
|
|
mReceiveThread.join();
|
|
}
|
|
}
|
|
|
|
void RokokoUDPReceiver::SetDataCallback(std::function<void(const std::vector<uint8_t>&, const std::string&)> callback)
|
|
{
|
|
mDataCallback = callback;
|
|
}
|
|
|
|
bool RokokoUDPReceiver::IsListening() const
|
|
{
|
|
return mIsListening;
|
|
}
|
|
|
|
bool RokokoUDPReceiver::IsConnected() const
|
|
{
|
|
return mIsListening && mPacketsReceived > 0;
|
|
}
|
|
|
|
std::string RokokoUDPReceiver::GetLastError() const
|
|
{
|
|
std::lock_guard<std::mutex> lock(mErrorMutex);
|
|
return mLastError;
|
|
}
|
|
|
|
void RokokoUDPReceiver::GetStatistics(uint64_t& packetsReceived, uint64_t& bytesReceived, double& lastPacketTime) const
|
|
{
|
|
std::lock_guard<std::mutex> lock(mStatsMutex);
|
|
packetsReceived = mPacketsReceived;
|
|
bytesReceived = mBytesReceived;
|
|
lastPacketTime = mLastPacketTime;
|
|
}
|
|
|
|
void RokokoUDPReceiver::ReceiveThread()
|
|
{
|
|
std::vector<uint8_t> buffer(mBufferSize);
|
|
|
|
while (mIsRunning) {
|
|
try {
|
|
struct sockaddr_in senderAddr;
|
|
int senderAddrSize = sizeof(senderAddr);
|
|
|
|
// UDP 데이터 수신
|
|
int bytesReceived = recvfrom(mSocket,
|
|
reinterpret_cast<char*>(buffer.data()),
|
|
mBufferSize,
|
|
0,
|
|
(struct sockaddr*)&senderAddr,
|
|
&senderAddrSize);
|
|
|
|
if (bytesReceived > 0) {
|
|
// 발신자 IP 주소 추출
|
|
char senderIP[INET_ADDRSTRLEN];
|
|
inet_ntop(AF_INET, &senderAddr.sin_addr, senderIP, INET_ADDRSTRLEN);
|
|
std::string senderIPStr(senderIP);
|
|
|
|
// 데이터 처리
|
|
ProcessIncomingData(buffer.data(), bytesReceived, senderIPStr);
|
|
|
|
// 통계 업데이트
|
|
UpdateStatistics(bytesReceived);
|
|
|
|
} else if (bytesReceived == SOCKET_ERROR) {
|
|
int error = WSAGetLastError();
|
|
if (error != WSAEWOULDBLOCK) {
|
|
// 실제 에러인 경우
|
|
std::ostringstream oss;
|
|
oss << "UDP receive error: " << error;
|
|
SetError(oss.str());
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 🚀 실시간성 최우선 - sleep 제거 (CPU 사용률보다 반응성 우선)
|
|
|
|
} catch (...) {
|
|
SetError("Exception in receive thread");
|
|
break;
|
|
}
|
|
}
|
|
|
|
mIsListening = false;
|
|
}
|
|
|
|
void RokokoUDPReceiver::ProcessIncomingData(const uint8_t* data, int size, const std::string& senderIP)
|
|
{
|
|
if (mDataCallback && data && size > 0) {
|
|
try {
|
|
// 데이터를 벡터로 복사
|
|
std::vector<uint8_t> dataCopy(data, data + size);
|
|
|
|
// 콜백 호출
|
|
mDataCallback(dataCopy, senderIP);
|
|
|
|
} catch (...) {
|
|
SetError("Exception in data callback");
|
|
}
|
|
}
|
|
}
|
|
|
|
void RokokoUDPReceiver::SetError(const std::string& error)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mErrorMutex);
|
|
mLastError = error;
|
|
|
|
// 에러 로깅 (OptiTrack에서 확인 가능)
|
|
std::cerr << "[RokokoUDPReceiver] Error: " << error << std::endl;
|
|
}
|
|
|
|
void RokokoUDPReceiver::UpdateStatistics(int packetSize)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mStatsMutex);
|
|
mPacketsReceived++;
|
|
mBytesReceived += packetSize;
|
|
mLastPacketTime = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
std::chrono::high_resolution_clock::now().time_since_epoch()
|
|
).count() / 1000.0;
|
|
}
|
|
|
|
void RokokoUDPReceiver::CloseSocket()
|
|
{
|
|
if (mSocket != INVALID_SOCKET) {
|
|
closesocket(mSocket);
|
|
mSocket = INVALID_SOCKET;
|
|
}
|
|
|
|
WSACleanup();
|
|
}
|
|
}
|