#include "TCPServer.h" #include ClientHandler::ClientHandler(int clientSocket, TCPServer* server) : clientSocket(clientSocket), server(server) {}; void ClientHandler::handle() { std::string buffer; buffer.reserve(4096); // Pre-allocate memory to avoid frequent allocations while (true) { char tempBuffer[4096] = {0}; ssize_t valread = recv(clientSocket, tempBuffer, sizeof(tempBuffer), 0); if (valread > 0) { buffer.append(tempBuffer, valread); //std::cout << "Received: " << buffer << std::endl; if (buffer == "quit") { std::cout << "Client requested to quit. Closing connection." << std::endl; break; } processMessage(buffer); buffer.clear(); } else if (valread == 0) { std::cout << "Client disconnected." << std::endl; break; // Client disconnected } else { std::cerr << "Failed to receive data." << std::endl; break; // Error in receiving data } } closeConnection(); } void ClientHandler::processMessage(const std::string& message) { server->handleMessage(message, clientSocket); } void ClientHandler::closeConnection() { close(clientSocket); server->clientDisconnected(clientSocket); // Inform the server that the client has disconnected } TCPServer::TCPServer(int port) { this->robotPose = {500, 500, -3.1415/2}; serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (serverSocket == -1) { std::cerr << "Socket creation failed" << std::endl; exit(EXIT_FAILURE); } sockaddr_in address{}; address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(port); if (bind(serverSocket, reinterpret_cast(&address), sizeof(address)) == -1) { std::cerr << "Binding failed" << std::endl; exit(EXIT_FAILURE); } if (listen(serverSocket, 5) == -1) { std::cerr << "Listening failed" << std::endl; exit(EXIT_FAILURE); } std::cout << "Server started on port " << port << std::endl; clients.reserve(7); ClientTCP tirette; tirette.name = "tirette"; ClientTCP aruco; aruco.name = "aruco"; ClientTCP ihm; ihm.name = "ihm"; ClientTCP lidar; lidar.name = "lidar"; ClientTCP arduino; arduino.name = "arduino"; ClientTCP servo_moteur; servo_moteur.name = "servo_moteur"; clients.push_back(tirette); clients.push_back(aruco); clients.push_back(ihm); clients.push_back(lidar); clients.push_back(arduino); clients.push_back(servo_moteur); } void TCPServer::acceptConnections() { while (!shouldStop) { sockaddr_in clientAddress{}; int addrlen = sizeof(clientAddress); int clientSocket = accept(serverSocket, reinterpret_cast(&clientAddress), reinterpret_cast(&addrlen)); if (clientSocket == -1) { std::cerr << "Accepting connection failed" << std::endl; continue; } std::cout << "Connection accepted" << std::endl; // Add the client socket to the list clientSockets.push_back(clientSocket); connectedClients++; // Handle client connection in a separate thread clientThreads.emplace_back(&ClientHandler::handle, ClientHandler(clientSocket, this)); // Clean up finished threads clientThreads.erase(std::remove_if(clientThreads.begin(), clientThreads.end(), [](std::thread &t) { return !t.joinable(); }), clientThreads.end()); } } void TCPServer::handleMessage(const std::string& message, int clientSocket) { std::vector tokens = split(message, ";"); if (tokens.size() != 4) { std::cerr << "Invalid message format : " << message << std::endl; return; } if (tokens[1] != "strat") { this->broadcastMessage(message.c_str(), clientSocket); } if (tokens[0] == "tirette" && tokens[2] == "set state") { this->broadcastMessage(message.c_str(), clientSocket); } if (tokens[2] == "ready") { for (ClientTCP& client : clients) { if (client.name == tokens[0]) { client.isReady = true; client.socket = clientSocket; break; } } checkIfAllClientsReady(); } if (tokens[2] == "set pos") { std::vector pos = split(tokens[3], ","); this->robotPose = {std::stof(pos[0]), std::stof(pos[1]), std::stof(pos[2]) / 100}; } if (tokens[2] == "get pos") { std::string toSend = "strat;all;set pos;" + std::to_string(this->robotPose.pos.x) + "," + std::to_string(this->robotPose.pos.y) + "," + std::to_string(this->robotPose.theta * 100) + "\n"; this->broadcastMessage(toSend, clientSocket); } if (tokens[2] == "spawn") { // TODO change that to handle spawn point this->robotPose = {500, 500, -1.57079}; std::string toSend = "strat;all;set pos;" + std::to_string(this->robotPose.pos.x) + "," + std::to_string(this->robotPose.pos.y) + "," + std::to_string(this->robotPose.theta * 100) + "\n"; this->broadcastMessage(toSend, clientSocket); } if (tokens[2] == "start") { this->broadcastMessage(message.c_str(), clientSocket); this->broadcastMessage("strat;arduino;speed;200\n"); this->actionNb = 0; this->startGame(); } if (tokens[0] == "aruco" && tokens[2] == "get aruco") { std::string arucoResponse = tokens[3]; if (arucoResponse != "404") { this->arucoTags.clear(); std::vector aruco = split(arucoResponse, ","); for (int i = 0; i < aruco.size() - 1; i += 7) { ArucoTagPos tag; tag.tag.id = std::stoi(aruco[i]); tag.tag.name = aruco[i + 1]; tag.pos.first[0] = std::stof(aruco[i + 2]); tag.pos.first[1] = std::stof(aruco[i + 3]); tag.pos.second[0] = std::stof(aruco[i + 4]); tag.pos.second[1] = std::stof(aruco[i + 5]); tag.pos.second[2] = std::stof(aruco[i + 6]); this->arucoTags.push_back(tag); } } this->waitForAruco = false; } // std::cout << "Received: " << message << std::endl; } void TCPServer::broadcastMessage(const char* message, int senderSocket) { std::string temp = message; this->broadcastMessage(temp, senderSocket); } void TCPServer::broadcastMessage(std::string &message, int senderSocket) { if (!endsWith(message, "\n")) { message += "\n"; } for (int clientSocket : clientSockets) { if (clientSocket != senderSocket) { // Exclude the sender's socket send(clientSocket, message.c_str(), message.size(), 0); } } } void TCPServer::clientDisconnected(const int clientSocket) { // Remove the disconnected client's socket clientSockets.erase(std::remove(clientSockets.begin(), clientSockets.end(), clientSocket), clientSockets.end()); // Decrement the count of connected clients connectedClients--; } void TCPServer::stop() { shouldStop = true; // Close all client sockets for (int clientSocket : clientSockets) { close(clientSocket); } // Close the server socket close(serverSocket); } TCPServer::~TCPServer() { this->stop(); // Join all threads before exiting for (auto& thread : clientThreads) { thread.join(); } } int TCPServer::nbClients() const { return connectedClients; } void TCPServer::start() { std::thread([this]() { acceptConnections(); }).detach(); std::thread([this]() { askArduinoPos(); }).detach(); } void TCPServer::checkIfAllClientsReady() { bool allReady = true; for (auto&[name, socket, isReady] : clients) { if (!isReady) { allReady = false; } } if (allReady) { this->broadcastMessage("strat;all;ready;1"); } } void TCPServer::startGame() { this->broadcastMessage("strat;servo_moteur;baisser bras;1\n"); this->broadcastMessage("strat;servo_moteur;fermer pince;1\n"); this->broadcastMessage("strat;servo_moteur;fermer pince;2\n"); this->broadcastMessage("strat;servo_moteur;ouvrir pince;0\n"); this->broadcastMessage("strat;arduino;speed;200\n"); waitForAruco = true; this->broadcastMessage("strat;aruco;get aruco;1\n"); std::cout << "Ask for aruco" << std::endl; while (this->waitForAruco) { usleep(100'000); } // ReSharper disable once CppDFAUnreachableCode if (this->arucoTags.empty()) { return; // TODO maybe rotate the robot but handle that here } std::cout << "Position " << this->robotPose.pos.x << " " << this->robotPose.pos.y << " " << this->robotPose.theta << std::endl; std::cout << "Aruco found" << std::endl; goToAruco(this->arucoTags[0], 1); std::cout << "I'm on aruco" << std::endl; std::cout << "Position " << this->robotPose.pos.x << " " << this->robotPose.pos.y << " " << this->robotPose.theta << std::endl; // pi/4 this->broadcastMessage("strat;arduino;angle;0\n"); this->broadcastMessage("strat;servo_moteur;baisser bras;1\n"); std::cout << "Position " << this->robotPose.pos.x << " " << this->robotPose.pos.y << " " << this->robotPose.theta << std::endl; usleep(2'000'000); std::cout << "An other aruco" << std::endl; waitForAruco = true; this->broadcastMessage("strat;aruco;get aruco;1\n"); while (this->waitForAruco) { usleep(100'000); } if (this->arucoTags.empty()) { return; } std::cout << "Position " << this->robotPose.pos.x << " " << this->robotPose.pos.y << " " << this->robotPose.theta << std::endl; std::cout << "Aruco found" << std::endl; goToAruco(this->arucoTags[0], 0); std::cout << "I'm on aruco" << std::endl; std::cout << "Position " << this->robotPose.pos.x << " " << this->robotPose.pos.y << " " << this->robotPose.theta << std::endl; this->broadcastMessage("strat;arduino;rotate;-78\n"); usleep(1'000'000); this->broadcastMessage("strat;arduino;go;500,500\n"); usleep(6'000'000); std::cout << "I'm on spawn point" << std::endl; // -pi/2 this->broadcastMessage("strat;arduino;angle;-157"); this->broadcastMessage("strat;servo_moteur;baisser bras;1"); this->broadcastMessage("strat;servo_moteur;clear;1"); std::cout << "End of game" << std::endl; } void TCPServer::goToAruco(const ArucoTagPos &arucoTagPos, const int pince) { float robotPosX = this->robotPose.pos.x; float robotPosY = this->robotPose.pos.y; float theta = this->robotPose.theta; float decalage; if (pince < 0 || pince > 2) { return; } switch (pince) { case 0: decalage = 80; break; case 1: decalage = 0; break; case 2: decalage = -80; break; default: decalage = 0; break; } this->broadcastMessage("strat;servo_moteur;baisser bras;1\n"); std::string toSend = "strat;servo_moteur;ouvrir pince;" + std::to_string(pince) + "\n"; this->broadcastMessage(toSend); double xPrime = arucoTagPos.pos.first[0] - 5; double yPrime = arucoTagPos.pos.first[1]; std::cout << "Aruco position1 " << xPrime << " " << yPrime << std::endl; double posV200X = ((xPrime - 100) * std::cos(theta) + (yPrime - decalage) * std::sin(theta)) + robotPosX; double posV200Y = (-(xPrime - 100) * std::sin(theta) + (yPrime - decalage) * std::cos(theta)) + robotPosY; toSend = "strat;arduino;go;" + std::to_string(static_cast(posV200X)) + "," + std::to_string(static_cast(posV200Y)) + "\n"; this->broadcastMessage(toSend); usleep(4'000'000); this->broadcastMessage("strat;arduino;speed;150\n"); usleep(500'000); double robotPosForPotX = (xPrime * std::cos(theta) + (yPrime - decalage) * std::sin(theta)) + robotPosX; double robotPosForPotY = (-xPrime * std::sin(theta) + (yPrime - decalage) * std::cos(theta)) + robotPosY; std::cout << "Aruco position " << robotPosForPotX << " " << robotPosForPotY << std::endl; toSend = "strat;arduino;go;" + std::to_string(static_cast(robotPosForPotX)) + "," + std::to_string(static_cast(robotPosForPotY)) + "\n"; this->broadcastMessage(toSend); usleep(3'000'000); std::cout << "end sleep" << std::endl; toSend = "strat;servo_moteur;fermer pince;" + std::to_string(pince) + "\n"; this->broadcastMessage(toSend); this->broadcastMessage("strat;servo_moteur;lever bras;1\n"); this->broadcastMessage("strat;arduino;speed;200\n"); havePot[pince] = true; } void TCPServer::askArduinoPos() { while (!this->shouldStop) { this->broadcastMessage("strat;arduino;get pos;1\n"); usleep(500'000); } }