From 1ec11b53108c5267c0872d1012765c353cdf1f44 Mon Sep 17 00:00:00 2001 From: Toby Roth Date: Thu, 1 Aug 2024 01:19:48 +0300 Subject: [PATCH 01/18] Communication: Write the basic communication code --- .gitignore | 26 +++++++++ CMakeLists.txt | 27 +++++++++ communication.cpp | 145 ++++++++++++++++++++++++++++++++++++++++++++++ communication.h | 19 ++++++ 4 files changed, 217 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 communication.cpp create mode 100644 communication.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..39591c23 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Ignore build directories +BUILD/ +build/ + +# Ignore log files +*.log + +# Ignore object files and executables +*.o +*.a +*.so +*.exe + +# Ignore temporary files +*.tmp +*.swp +*.swo + +# Ignore IDE-specific files +.vscode/ +*.sublime-workspace +*.sublime-project + +# Ignore system-specific files +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..23aadac0 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.10) + +# Project name +project(BasicApi) + +# Setting standards +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# Adding libraries and includes +include_directories(${CMAKE_SOURCE_DIR}) + +# Adding source files +set(COMMON_SOURCES + communication.cpp + myClass.cpp +) + +# Creating an executable file for user1 +add_executable(user1 user1.cpp ${COMMON_SOURCES}) + +# Creating an executable file for user2 +add_executable(user2 user2.cpp ${COMMON_SOURCES}) + +# Linking with the pthread library +target_link_libraries(user1 pthread) +target_link_libraries(user2 pthread) diff --git a/communication.cpp b/communication.cpp new file mode 100644 index 00000000..21a6b14e --- /dev/null +++ b/communication.cpp @@ -0,0 +1,145 @@ +#include "communication.h" +#include +#include +#include +#include +#include +#include + +#define IP_ADDRESS "127.0.0.1" +#define BACKLOG 3 +const size_t communication::PACKET_SIZE = 16; + +// Receive messages from a socket +void communication::receiveMessages(int socketFd) +{ + uint8_t buffer[PACKET_SIZE] = {0}; + std::cout << "before the while in receive func" << std::endl; // Before entering the loop in the receive function + + while (true) { + int valread = recv(socketFd, buffer, PACKET_SIZE, 0); + if (valread <= 0) { + std::cerr << "Connection closed or error occurred" << std::endl; // Connection closed or an error occurred + break; + } + + std::cout << "Received packet: "; + for (int i = 0; i < valread; ++i) { + std::cout << static_cast(buffer[i]) << " "; + } + std::cout << std::endl; + } +} + +// Send messages through a socket +void communication::sendMessage(int socketFd, void* data, size_t dataLen) +{ + uint8_t buffer[PACKET_SIZE] = {0}; + size_t offset = 0; + + while (offset < dataLen) { + size_t packetLen = std::min(dataLen - offset, PACKET_SIZE); + std::memcpy(buffer, static_cast(data) + offset, packetLen); + int sendAns = send(socketFd, buffer, packetLen, 0); + if(sendAns < 0){ + std::cerr << "The send failed" << std::endl; // The send failed + break; + } + offset += packetLen; + } +} + +// Initialize a connection and set up listening +int communication::initConnection(int portNumber) { + int peerPort = (portNumber == PORT1) ? PORT2 : PORT1; + + int sockFd, newSocket; + struct sockaddr_in address, peerAddr; + int opt = 1; + int addrlen = sizeof(address); + + sockFd = socket(AF_INET, SOCK_STREAM, 0); + if (sockFd < 0) { + perror("Socket creation error"); + return -1; + } + + int sockopt = setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); + if (sockopt) { + perror("setsockopt"); + close(sockFd); + return -1; + } + + memset(&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(portNumber); + + int bindAns = bind(sockFd, (struct sockaddr*)&address, sizeof(address)); + if (bindAns < 0) { + perror("Bind failed"); + close(sockFd); + return -1; + } + + int listenAns = listen(sockFd, BACKLOG); + if (listenAns < 0) { + perror("Listen"); + close(sockFd); + return -1; + } + + std::cout << "Listening on port " << portNumber << std::endl; + + memset(&peerAddr, 0, sizeof(peerAddr)); + peerAddr.sin_family = AF_INET; + peerAddr.sin_port = htons(peerPort); + + int inetAns = inet_pton(AF_INET, IP_ADDRESS, &peerAddr.sin_addr); + if (inetAns <= 0) { + perror("Invalid address/ Address not supported"); + return -1; + } + + int clientSock = -1; + std::thread recvThread; + + if (portNumber == PORT1) { + std::this_thread::sleep_for(std::chrono::seconds(1)); // Waiting for the other process to listen + clientSock = socket(AF_INET, SOCK_STREAM, 0); + + if (connect(clientSock, (struct sockaddr*)&peerAddr, sizeof(peerAddr)) < 0) { + perror("Connection Failed"); + return -1; + } + + newSocket = accept(sockFd, (struct sockaddr*)&address, (socklen_t*)&addrlen); + if (newSocket < 0) { + perror("Accept failed"); + return -1; + } + + recvThread = std::thread(&communication::receiveMessages, this, newSocket); + + } else { + newSocket = accept(sockFd, (struct sockaddr*)&address, (socklen_t*)&addrlen); + if (newSocket < 0) { + perror("Accept failed"); + return -1; + } + + recvThread = std::thread(&communication::receiveMessages, this, newSocket); + std::this_thread::sleep_for(std::chrono::seconds(1)); // Waiting for the server to listen + + clientSock = socket(AF_INET, SOCK_STREAM, 0); + if (connect(clientSock, (struct sockaddr*)&peerAddr, sizeof(peerAddr)) < 0) { + perror("Connection Failed"); + return -1; + } + } + + recvThread.detach(); + std::cout << "Connection initialized" << std::endl; + return clientSock; +} diff --git a/communication.h b/communication.h new file mode 100644 index 00000000..8182b397 --- /dev/null +++ b/communication.h @@ -0,0 +1,19 @@ +#ifndef COMMUNICATION_H +#define COMMUNICATION_H + +#include +#include + +class communication { +public: + int initConnection(int portNumber); + void sendMessage(int socketFd, void* data, size_t dataLen); + void receiveMessages(int socketFd); + +private: + const int PORT1 = 8080; + const int PORT2 = 8081; + static const size_t PACKET_SIZE; +}; + +#endif From c1ef440f8004d6dc6914e30fd9a5b0b1c18508ab Mon Sep 17 00:00:00 2001 From: devora6936 Date: Tue, 30 Jul 2024 17:17:38 +0300 Subject: [PATCH 02/18] Communication: Allow each proccess to start --- CMakeLists.txt | 1 + communication.cpp | 136 ++++++++++++++++++++++++++++++++++------------ communication.h | 15 ++++- 3 files changed, 115 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 23aadac0..7a497006 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ cmake_minimum_required(VERSION 3.10) project(BasicApi) # Setting standards + set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) diff --git a/communication.cpp b/communication.cpp index 21a6b14e..47bbba81 100644 --- a/communication.cpp +++ b/communication.cpp @@ -5,10 +5,17 @@ #include #include #include +#include #define IP_ADDRESS "127.0.0.1" #define BACKLOG 3 + +// Constants for packet size and state file names + const size_t communication::PACKET_SIZE = 16; +const char* communication::STATE_FILE = "comm_state3.txt"; +const char* communication::LOCK_FILE = "comm_state3.lock"; +std::mutex communication::state_file_mutex; // Receive messages from a socket void communication::receiveMessages(int socketFd) @@ -49,12 +56,29 @@ void communication::sendMessage(int socketFd, void* data, size_t dataLen) } } -// Initialize a connection and set up listening -int communication::initConnection(int portNumber) { - int peerPort = (portNumber == PORT1) ? PORT2 : PORT1; +// Initialize the state based on the presence of the state file +void communication::initializeState(int& portNumber, int& peerPort) +{ + std::lock_guard lock(state_file_mutex); + std::ifstream infile(STATE_FILE); + if (!infile) { // If the file doesn't exist - means this is the first process + portNumber = PORT2; + peerPort = PORT1; + std::ofstream outfile(STATE_FILE); + outfile << "initialized"; + outfile.close(); + std::ofstream lockFile(LOCK_FILE); + lockFile.close(); + } else { // If the file already exists - means this is the second process + portNumber = PORT1; + peerPort = PORT2; + } + infile.close(); +} - int sockFd, newSocket; - struct sockaddr_in address, peerAddr; +// Set up a socket for listening +int communication::setupSocket(int portNumber, int& sockFd, struct sockaddr_in& address) +{ int opt = 1; int addrlen = sizeof(address); @@ -91,10 +115,30 @@ int communication::initConnection(int portNumber) { } std::cout << "Listening on port " << portNumber << std::endl; + return 0; +} - memset(&peerAddr, 0, sizeof(peerAddr)); - peerAddr.sin_family = AF_INET; - peerAddr.sin_port = htons(peerPort); +// Wait for an incoming connection +int communication::waitForConnection(int sockFd, struct sockaddr_in& address) +{ + int newSocket; + int addrlen = sizeof(address); + newSocket = accept(sockFd, (struct sockaddr*)&address, (socklen_t*)&addrlen); + if (newSocket < 0) { + perror("Accept failed"); + return -1; + } + return newSocket; +} + +// Connect to a peer socket +int communication::connectToPeer(int portNumber, struct sockaddr_in& peerAddr) +{ + int clientSock = socket(AF_INET, SOCK_STREAM, 0); + if (clientSock < 0) { + perror("Socket creation error"); + return -1; + } int inetAns = inet_pton(AF_INET, IP_ADDRESS, &peerAddr.sin_addr); if (inetAns <= 0) { @@ -102,44 +146,68 @@ int communication::initConnection(int portNumber) { return -1; } + int connectAns = connect(clientSock, (struct sockaddr*)&peerAddr, sizeof(peerAddr)); + if (connectAns < 0) { + perror("Connection Failed"); + return -1; + } + + return clientSock; +} + +// Initialize the connection +int communication::initConnection() +{ + int portNumber, peerPort; + initializeState(portNumber, peerPort); + + int sockFd, newSocket; + struct sockaddr_in address, peerAddr; + + int setupSocketAns = setupSocket(portNumber, sockFd, address); + if (setupSocketAns < 0) { + return -1; + } + + memset(&peerAddr, 0, sizeof(peerAddr)); + peerAddr.sin_family = AF_INET; + peerAddr.sin_port = htons(peerPort); + int clientSock = -1; std::thread recvThread; if (portNumber == PORT1) { - std::this_thread::sleep_for(std::chrono::seconds(1)); // Waiting for the other process to listen - clientSock = socket(AF_INET, SOCK_STREAM, 0); - - if (connect(clientSock, (struct sockaddr*)&peerAddr, sizeof(peerAddr)) < 0) { - perror("Connection Failed"); - return -1; - } - - newSocket = accept(sockFd, (struct sockaddr*)&address, (socklen_t*)&addrlen); - if (newSocket < 0) { - perror("Accept failed"); - return -1; - } + // Wait briefly to ensure the second process (PORT2) starts listening + std::this_thread::sleep_for(std::chrono::seconds(1)); + + clientSock = connectToPeer(portNumber, peerAddr); + if (clientSock < 0) return -1; + // Wait for an incoming connection from PORT2 + newSocket = waitForConnection(sockFd, address); + if (newSocket < 0) return -1; + + // Start a separate thread to handle incoming messages on the new socket recvThread = std::thread(&communication::receiveMessages, this, newSocket); } else { - newSocket = accept(sockFd, (struct sockaddr*)&address, (socklen_t*)&addrlen); - if (newSocket < 0) { - perror("Accept failed"); - return -1; - } - + // Wait for an incoming connection from PORT1 + newSocket = waitForConnection(sockFd, address); + if (newSocket < 0) return -1; + + // Start a separate thread to handle incoming messages on the new socket recvThread = std::thread(&communication::receiveMessages, this, newSocket); std::this_thread::sleep_for(std::chrono::seconds(1)); // Waiting for the server to listen - - clientSock = socket(AF_INET, SOCK_STREAM, 0); - if (connect(clientSock, (struct sockaddr*)&peerAddr, sizeof(peerAddr)) < 0) { - perror("Connection Failed"); - return -1; - } + + clientSock = connectToPeer(portNumber, peerAddr); + if (clientSock < 0) return -1; + + // Clean up: remove state and lock files as they are no longer needed + std::remove(STATE_FILE); + std::remove(LOCK_FILE); } recvThread.detach(); std::cout << "Connection initialized" << std::endl; return clientSock; -} +} \ No newline at end of file diff --git a/communication.h b/communication.h index 8182b397..b4ba743e 100644 --- a/communication.h +++ b/communication.h @@ -3,17 +3,26 @@ #include #include +#include class communication { public: - int initConnection(int portNumber); + int initConnection(); void sendMessage(int socketFd, void* data, size_t dataLen); void receiveMessages(int socketFd); - + private: + int setupSocket(int portNumber, int& sockFd, struct sockaddr_in& address); + int waitForConnection(int sockFd, struct sockaddr_in& address); + int connectToPeer(int portNumber, struct sockaddr_in& peerAddr); + void initializeState(int& portNumber, int& peerPort); + const int PORT1 = 8080; const int PORT2 = 8081; static const size_t PACKET_SIZE; + static const char* STATE_FILE; + static const char* LOCK_FILE; + static std::mutex state_file_mutex; }; -#endif +#endif \ No newline at end of file From 2d2842c7089cf3a062115be410826aa791df67c0 Mon Sep 17 00:00:00 2001 From: devora6936 Date: Wed, 31 Jul 2024 12:45:22 +0300 Subject: [PATCH 03/18] Communication: Trigger the reciever when a new message arrives --- CMakeLists.txt | 1 + Packet.cpp | 31 +++++++++++++++++ Packet.h | 18 ++++++++++ communication.cpp | 84 +++++++++++++++++++++++++++++++++++++++-------- communication.h | 19 +++++++++-- 5 files changed, 137 insertions(+), 16 deletions(-) create mode 100644 Packet.cpp create mode 100644 Packet.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a497006..1e7965c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ include_directories(${CMAKE_SOURCE_DIR}) set(COMMON_SOURCES communication.cpp myClass.cpp + Packet.cpp ) # Creating an executable file for user1 diff --git a/Packet.cpp b/Packet.cpp new file mode 100644 index 00000000..6eb4c6ab --- /dev/null +++ b/Packet.cpp @@ -0,0 +1,31 @@ +#include "Packet.h" + +int Packet::getPsn() const +{ + return PSN; +} + +int Packet::getTotalPacketSum() const +{ + return totalPacketSum; +} + +uint8_t* Packet::getData() +{ + return data; +} + +void Packet::setPsn(int psn) +{ + this->PSN = psn; +} + +void Packet::setTotalPacketSum(int totalPacketSum) +{ + this->totalPacketSum = totalPacketSum; +} + +void Packet::setData(const void* inputData, size_t size) +{ + std::memcpy(this->data, inputData, size); +} \ No newline at end of file diff --git a/Packet.h b/Packet.h new file mode 100644 index 00000000..a409c369 --- /dev/null +++ b/Packet.h @@ -0,0 +1,18 @@ +#include +#include +#include "communication.h" + +class Packet { +public: + int getPsn() const; + int getTotalPacketSum() const; + uint8_t* getData(); + void setPsn(int psn); + void setTotalPacketSum(int totalPacketSum); + void setData(const void* inputData, size_t size); + +private: + int PSN = 0; + int totalPacketSum = 0; + uint8_t data[communication::PACKET_SIZE] = {0}; +}; diff --git a/communication.cpp b/communication.cpp index 47bbba81..f895e094 100644 --- a/communication.cpp +++ b/communication.cpp @@ -1,4 +1,5 @@ #include "communication.h" +#include "Packet.h" #include #include #include @@ -6,54 +7,109 @@ #include #include #include +#include +#include #define IP_ADDRESS "127.0.0.1" #define BACKLOG 3 // Constants for packet size and state file names -const size_t communication::PACKET_SIZE = 16; const char* communication::STATE_FILE = "comm_state3.txt"; const char* communication::LOCK_FILE = "comm_state3.lock"; std::mutex communication::state_file_mutex; +// Send acknowledgment messages +void communication::sendAck(int socketFd, AckType ackType) +{ + const char* ackMessage = (ackType == AckType::ACK) ? "ACK" : "NACK"; + send(socketFd, ackMessage, strlen(ackMessage), 0); + + // Trigger the acknowledgment callback + if (ackCallback) { + ackCallback(ackType); + } +} + +// Set the callback for data received +void communication::setDataHandler(std::function&)> callback) +{ + dataHandler = callback; +} + +// Set the callback for acknowledgment received +void communication::setAckCallback(std::function callback) +{ + ackCallback = callback; +} + // Receive messages from a socket void communication::receiveMessages(int socketFd) { - uint8_t buffer[PACKET_SIZE] = {0}; - std::cout << "before the while in receive func" << std::endl; // Before entering the loop in the receive function - + Packet packet; + std::vector receivedData; while (true) { - int valread = recv(socketFd, buffer, PACKET_SIZE, 0); + int valread = recv(socketFd, &packet, sizeof(Packet), 0); if (valread <= 0) { std::cerr << "Connection closed or error occurred" << std::endl; // Connection closed or an error occurred break; } - std::cout << "Received packet: "; + // Append the received data to the vector for (int i = 0; i < valread; ++i) { - std::cout << static_cast(buffer[i]) << " "; + if (i < sizeof(packet.getData())) { + receivedData.push_back(packet.getData()[i]); + } + } + + // Notify the user via callback function + if (dataHandler) { + dataHandler(receivedData); + } + + // Check if all packets have been received + if (packet.getPsn() == packet.getTotalPacketSum()) { + sendAck(socketFd, AckType::ACK); } - std::cout << std::endl; } } -// Send messages through a socket +//Send message in a socket void communication::sendMessage(int socketFd, void* data, size_t dataLen) { - uint8_t buffer[PACKET_SIZE] = {0}; + Packet packet; + int psn = 0; size_t offset = 0; - + size_t totalPackets = (dataLen + PACKET_SIZE - 1) / PACKET_SIZE; + while (offset < dataLen) { size_t packetLen = std::min(dataLen - offset, PACKET_SIZE); - std::memcpy(buffer, static_cast(data) + offset, packetLen); - int sendAns = send(socketFd, buffer, packetLen, 0); + packet.setData(static_cast(data) + offset, packetLen); + packet.setPsn(psn++); + packet.setTotalPacketSum(totalPackets - 1); + + int sendAns = send(socketFd, &packet, sizeof(Packet), 0); if(sendAns < 0){ std::cerr << "The send failed" << std::endl; // The send failed break; } offset += packetLen; } + + // Wait for acknowledgment + char buffer[4]; + int valread = recv(socketFd, buffer, sizeof(buffer), 0); + if (valread > 0) { + buffer[valread] = '\0'; // Null-terminate the received string + AckType receivedAck = (strcmp(buffer, "ACK") == 0) ? AckType::ACK : AckType::NACK; + + // Trigger the acknowledgment callback + if (ackCallback) { + ackCallback(receivedAck); + } + } else { + std::cerr << "Failed to receive acknowledgment" << std::endl; + } } // Initialize the state based on the presence of the state file @@ -61,6 +117,7 @@ void communication::initializeState(int& portNumber, int& peerPort) { std::lock_guard lock(state_file_mutex); std::ifstream infile(STATE_FILE); + if (!infile) { // If the file doesn't exist - means this is the first process portNumber = PORT2; peerPort = PORT1; @@ -208,6 +265,5 @@ int communication::initConnection() } recvThread.detach(); - std::cout << "Connection initialized" << std::endl; return clientSock; } \ No newline at end of file diff --git a/communication.h b/communication.h index b4ba743e..09a7cdb3 100644 --- a/communication.h +++ b/communication.h @@ -4,25 +4,40 @@ #include #include #include +#include +#include +#include + +// Enum for acknowledgment types +enum class AckType { + ACK, + NACK +}; class communication { public: int initConnection(); void sendMessage(int socketFd, void* data, size_t dataLen); void receiveMessages(int socketFd); - + void setDataHandler(std::function&)> callback); + void setAckCallback(std::function callback); + static constexpr size_t PACKET_SIZE = 16; + private: int setupSocket(int portNumber, int& sockFd, struct sockaddr_in& address); int waitForConnection(int sockFd, struct sockaddr_in& address); int connectToPeer(int portNumber, struct sockaddr_in& peerAddr); void initializeState(int& portNumber, int& peerPort); + void sendAck(int socketFd, AckType ackType); const int PORT1 = 8080; const int PORT2 = 8081; - static const size_t PACKET_SIZE; static const char* STATE_FILE; static const char* LOCK_FILE; static std::mutex state_file_mutex; + std::vector dataReceived; + std::function&)> dataHandler; + std::function ackCallback; }; #endif \ No newline at end of file From 3c100595e0466c05c2b25c20288b85c6a92c27e0 Mon Sep 17 00:00:00 2001 From: ayala-freind <0527688937a@gmail.com> Date: Wed, 31 Jul 2024 13:13:07 +0300 Subject: [PATCH 04/18] Communication: Add testing and loging --- CMakeLists.txt | 50 ++- communication.h | 43 --- sockets/ISocket.h | 20 ++ sockets/mockSocket.h | 21 ++ sockets/realSocket.h | 107 +++++++ Packet.cpp => src/Packet.cpp | 7 +- Packet.h => src/Packet.h | 1 + communication.cpp => src/communication.cpp | 185 ++++++----- src/communication.h | 50 +++ test/testCommunication.cpp | 350 +++++++++++++++++++++ 10 files changed, 691 insertions(+), 143 deletions(-) delete mode 100644 communication.h create mode 100644 sockets/ISocket.h create mode 100644 sockets/mockSocket.h create mode 100644 sockets/realSocket.h rename Packet.cpp => src/Packet.cpp (84%) rename Packet.h => src/Packet.h (94%) rename communication.cpp => src/communication.cpp (51%) create mode 100644 src/communication.h create mode 100644 test/testCommunication.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e7965c0..754bf7c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,29 +1,27 @@ cmake_minimum_required(VERSION 3.10) - -# Project name -project(BasicApi) - -# Setting standards - +project(basicApi) +# Set C++ standard set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED True) - -# Adding libraries and includes -include_directories(${CMAKE_SOURCE_DIR}) - -# Adding source files -set(COMMON_SOURCES - communication.cpp - myClass.cpp - Packet.cpp +# Add source files +set(SOURCES + src/Packet.cpp + src/communication.cpp + test/testCommunication.cpp ) - -# Creating an executable file for user1 -add_executable(user1 user1.cpp ${COMMON_SOURCES}) - -# Creating an executable file for user2 -add_executable(user2 user2.cpp ${COMMON_SOURCES}) - -# Linking with the pthread library -target_link_libraries(user1 pthread) -target_link_libraries(user2 pthread) +# Include directories +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/sockets) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/test) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../googletest/googletest/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../googletest/googlemock/include) +# Find and link pthread +find_package(Threads REQUIRED) +# Add GTest and GMock +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../googletest googletest-build) +# Add the test executable +add_executable(testCommunication ${SOURCES}) +target_link_libraries(testCommunication gtest gmock gtest_main gmock_main Threads::Threads) +# Enable testing +enable_testing() +# Add tests +add_test(NAME testCommunication COMMAND testCommunication) \ No newline at end of file diff --git a/communication.h b/communication.h deleted file mode 100644 index 09a7cdb3..00000000 --- a/communication.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef COMMUNICATION_H -#define COMMUNICATION_H - -#include -#include -#include -#include -#include -#include - -// Enum for acknowledgment types -enum class AckType { - ACK, - NACK -}; - -class communication { -public: - int initConnection(); - void sendMessage(int socketFd, void* data, size_t dataLen); - void receiveMessages(int socketFd); - void setDataHandler(std::function&)> callback); - void setAckCallback(std::function callback); - static constexpr size_t PACKET_SIZE = 16; - -private: - int setupSocket(int portNumber, int& sockFd, struct sockaddr_in& address); - int waitForConnection(int sockFd, struct sockaddr_in& address); - int connectToPeer(int portNumber, struct sockaddr_in& peerAddr); - void initializeState(int& portNumber, int& peerPort); - void sendAck(int socketFd, AckType ackType); - - const int PORT1 = 8080; - const int PORT2 = 8081; - static const char* STATE_FILE; - static const char* LOCK_FILE; - static std::mutex state_file_mutex; - std::vector dataReceived; - std::function&)> dataHandler; - std::function ackCallback; -}; - -#endif \ No newline at end of file diff --git a/sockets/ISocket.h b/sockets/ISocket.h new file mode 100644 index 00000000..e086bf99 --- /dev/null +++ b/sockets/ISocket.h @@ -0,0 +1,20 @@ + +#ifndef ISOCKET_H +#define ISOCKET_H + +#include + +class ISocket { +public: + virtual int socket(int domain, int type, int protocol) = 0; + virtual int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) = 0; + virtual int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) = 0; + virtual int listen(int sockfd, int backlog) = 0; + virtual int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) = 0; + virtual int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) = 0; + virtual ssize_t send(int sockfd, const void *buf, size_t len, int flags) = 0; + virtual ssize_t recv(int sockfd, void *buf, size_t len, int flags) = 0; + virtual int close(int fd) = 0; +}; + +#endif // ISOCKET_H \ No newline at end of file diff --git a/sockets/mockSocket.h b/sockets/mockSocket.h new file mode 100644 index 00000000..d2596ff6 --- /dev/null +++ b/sockets/mockSocket.h @@ -0,0 +1,21 @@ + +#ifndef MOCKSOCKET_H +#define MOCKSOCKET_H + +#include "gmock/gmock.h" +#include "ISocket.h" + +class MockSocket : public ISocket { +public: + MOCK_METHOD(int, socket, (int domain, int type, int protocol), (override)); + MOCK_METHOD(int, setsockopt, (int sockfd, int level, int optname, const void *optval, socklen_t optlen), (override)); + MOCK_METHOD(int, bind, (int sockfd, const struct sockaddr *addr, socklen_t addrlen), (override)); + MOCK_METHOD(int, listen, (int sockfd, int backlog), (override)); + MOCK_METHOD(int, accept, (int sockfd, struct sockaddr *addr, socklen_t *addrlen), (override)); + MOCK_METHOD(int, connect, (int sockfd, const struct sockaddr *addr, socklen_t addrlen), (override)); + MOCK_METHOD(ssize_t, send, (int sockfd, const void *buf, size_t len, int flags), (override)); + MOCK_METHOD(ssize_t, recv, (int sockfd, void *buf, size_t len, int flags), (override)); + MOCK_METHOD(int, close, (int fd), (override)); +}; + +#endif // MOCKSOCKET_H \ No newline at end of file diff --git a/sockets/realSocket.h b/sockets/realSocket.h new file mode 100644 index 00000000..d5c39bd4 --- /dev/null +++ b/sockets/realSocket.h @@ -0,0 +1,107 @@ +#ifndef REALSOCKET_H +#define REALSOCKET_H + +#include "ISocket.h" +#include +#include + +class RealSocket : public ISocket +{ +public: + int socket(int domain, int type, int protocol) override + { + int sockFd = ::socket(domain, type, protocol); + if (sockFd < 0) + { + communication::logMessage("Server", "Client", "[ERROR] Socket creation error: " + std::string(strerror(errno))); + } + + return sockFd; + } + + int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) override + { + int sockopt = ::setsockopt(sockfd, level, optname, optval, optlen); + if (sockopt) + { + communication::logMessage("Server", "Client", "[ERROR] setsockopt failed: " + std::string(strerror(errno))); + close(sockfd); + } + + return sockopt; + } + + int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) override + { + int bindAns = ::bind(sockfd, addr, addrlen); + if (bindAns < 0) + { + communication::logMessage("Server", "Client", "[ERROR] Bind failed: " + std::string(strerror(errno))); + close(sockfd); + } + + return bindAns; + } + + int listen(int sockfd, int backlog) override + { + int listenAns = ::listen(sockfd, backlog); + if (listenAns < 0) + { + communication::logMessage("Server", "Client", "[ERROR] Listen failed: " + std::string(strerror(errno))); + close(sockfd); + } + + return listenAns; + } + + int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) override + { + int newSocket = ::accept(sockfd, addr, addrlen); + if (newSocket < 0) + { + communication::logMessage("Server", "Client", "[ERROR] Accept failed: " + std::string(strerror(errno))); + } + + return newSocket; + } + + int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) override + { + int connectAns = ::connect(sockfd, addr, addrlen); + if (connectAns < 0) + { + communication::logMessage("Client", "Server", "[ERROR] Connection Failed: " + std::string(strerror(errno))); + } + + return connectAns; + } + + ssize_t recv(int sockfd, void *buf, size_t len, int flags) override + { + int valread = ::recv(sockfd, buf, len, flags); + if (valread <= 0) + { + communication::logMessage("Server", "Client", "[ERROR] Connection closed or error occurred: " + std::string(strerror(errno))); + } + + return valread; + } + + ssize_t send(int sockfd, const void *buf, size_t len, int flags) override + { + int sendAns = ::send(sockfd, buf, len, flags); + if(sendAns < 0){ + communication::logMessage("Server", "Client", "[ERROR] Failed to send acknowledgment: " + std::string(strerror(errno))); + } + + return sendAns; + } + + int close(int fd) override + { + return ::close(fd); + } +}; + +#endif // REALSOCKET_H \ No newline at end of file diff --git a/Packet.cpp b/src/Packet.cpp similarity index 84% rename from Packet.cpp rename to src/Packet.cpp index 6eb4c6ab..c7d3d549 100644 --- a/Packet.cpp +++ b/src/Packet.cpp @@ -28,4 +28,9 @@ void Packet::setTotalPacketSum(int totalPacketSum) void Packet::setData(const void* inputData, size_t size) { std::memcpy(this->data, inputData, size); -} \ No newline at end of file +} + +void Packet::resetData() +{ + memset(data, '\0', communication::PACKET_SIZE); +} diff --git a/Packet.h b/src/Packet.h similarity index 94% rename from Packet.h rename to src/Packet.h index a409c369..8b89f42f 100644 --- a/Packet.h +++ b/src/Packet.h @@ -10,6 +10,7 @@ class Packet { void setPsn(int psn); void setTotalPacketSum(int totalPacketSum); void setData(const void* inputData, size_t size); + void resetData(); private: int PSN = 0; diff --git a/communication.cpp b/src/communication.cpp similarity index 51% rename from communication.cpp rename to src/communication.cpp index f895e094..a36011e1 100644 --- a/communication.cpp +++ b/src/communication.cpp @@ -9,30 +9,58 @@ #include #include #include +#include +#include +#include +#include #define IP_ADDRESS "127.0.0.1" #define BACKLOG 3 // Constants for packet size and state file names - -const char* communication::STATE_FILE = "comm_state3.txt"; -const char* communication::LOCK_FILE = "comm_state3.lock"; +const char *communication::STATE_FILE = "../../comm_state3.txt"; +const char *communication::LOCK_FILE = "../../comm_state3.lock"; std::mutex communication::state_file_mutex; -// Send acknowledgment messages -void communication::sendAck(int socketFd, AckType ackType) +// Function to generate a timestamped log file name +std::string communication::getLogFileName() { - const char* ackMessage = (ackType == AckType::ACK) ? "ACK" : "NACK"; - send(socketFd, ackMessage, strlen(ackMessage), 0); + auto now = std::chrono::system_clock::now(); + auto time = std::chrono::system_clock::to_time_t(now); + std::tm tm = *std::localtime(&time); + + std::ostringstream oss; + oss << "../../" << std::put_time(&tm, "%Y_%m_%d_%H_%M_%S") << "_communication.log"; + return oss.str(); +} + +communication::communication(ISocket *socketInterface) : socketInterface(socketInterface) {} - // Trigger the acknowledgment callback - if (ackCallback) { - ackCallback(ackType); +void communication::logMessage(const std::string &src, const std::string &dst, const std::string &message) +{ + std::string logFileName = getLogFileName(); + std::ofstream logFile(logFileName, std::ios_base::app); + if (!logFile) { + std::cerr << "[ERROR] Failed to open log file" << std::endl; + return; } + + auto now = std::chrono::system_clock::now(); + auto time = std::chrono::system_clock::to_time_t(now); + std::tm tm = *std::localtime(&time); + logFile << "[" << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << "] " + << "SRC: " << src << " DST: " << dst << " " << message << std::endl; +} + +// Send acknowledgment messages +void communication::sendAck(int socketFd, AckType ackType) +{ + const char *ackMessage = (ackType == AckType::ACK) ? "ACK" : "NACK"; + socketInterface->send(socketFd, ackMessage, strlen(ackMessage), 0); } // Set the callback for data received -void communication::setDataHandler(std::function&)> callback) +void communication::setDataHandler(std::function &)> callback) { dataHandler = callback; } @@ -49,24 +77,23 @@ void communication::receiveMessages(int socketFd) Packet packet; std::vector receivedData; while (true) { - int valread = recv(socketFd, &packet, sizeof(Packet), 0); - if (valread <= 0) { - std::cerr << "Connection closed or error occurred" << std::endl; // Connection closed or an error occurred - break; - } + int valread = socketInterface->recv(socketFd, &packet, sizeof(Packet), 0); + std::string receivePacketForLog = ""; // Append the received data to the vector - for (int i = 0; i < valread; ++i) { - if (i < sizeof(packet.getData())) { - receivedData.push_back(packet.getData()[i]); - } + for (int i = 0; i < PACKET_SIZE; ++i) { + receivedData.push_back(packet.getData()[i]); + receivePacketForLog += packet.getData()[i]; } + logMessage("Server", "Client", "[INFO] " + receivePacketForLog); // Notify the user via callback function if (dataHandler) { dataHandler(receivedData); } + receivedData.clear(); + // Check if all packets have been received if (packet.getPsn() == packet.getTotalPacketSum()) { sendAck(socketFd, AckType::ACK); @@ -74,9 +101,16 @@ void communication::receiveMessages(int socketFd) } } -//Send message in a socket -void communication::sendMessage(int socketFd, void* data, size_t dataLen) +// Send message in a socket +void communication::sendMessage(int socketFd, void *data, size_t dataLen) { + if (data == nullptr || dataLen == 0) { + if (ackCallback) { + ackCallback(AckType::NACK); + } + return; + } + Packet packet; int psn = 0; size_t offset = 0; @@ -84,49 +118,50 @@ void communication::sendMessage(int socketFd, void* data, size_t dataLen) while (offset < dataLen) { size_t packetLen = std::min(dataLen - offset, PACKET_SIZE); - packet.setData(static_cast(data) + offset, packetLen); + packet.setData(static_cast(data) + offset, packetLen); packet.setPsn(psn++); packet.setTotalPacketSum(totalPackets - 1); - int sendAns = send(socketFd, &packet, sizeof(Packet), 0); - if(sendAns < 0){ - std::cerr << "The send failed" << std::endl; // The send failed + int sendAns = socketInterface->send(socketFd, &packet, sizeof(Packet), 0); + if (sendAns < 0) { break; } + offset += packetLen; + packet.resetData(); } - // Wait for acknowledgment char buffer[4]; - int valread = recv(socketFd, buffer, sizeof(buffer), 0); + int valread = socketInterface->recv(socketFd, buffer, sizeof(buffer), 0); if (valread > 0) { buffer[valread] = '\0'; // Null-terminate the received string AckType receivedAck = (strcmp(buffer, "ACK") == 0) ? AckType::ACK : AckType::NACK; - + logMessage("Client", "Server", "[INFO] " + (std::string)buffer); // Trigger the acknowledgment callback if (ackCallback) { ackCallback(receivedAck); } - } else { - std::cerr << "Failed to receive acknowledgment" << std::endl; } } // Initialize the state based on the presence of the state file -void communication::initializeState(int& portNumber, int& peerPort) +void communication::initializeState(int &portNumber, int &peerPort) { std::lock_guard lock(state_file_mutex); std::ifstream infile(STATE_FILE); - if (!infile) { // If the file doesn't exist - means this is the first process + if (!infile) { + // If the file doesn't exist - means this is the first process portNumber = PORT2; peerPort = PORT1; std::ofstream outfile(STATE_FILE); outfile << "initialized"; - outfile.close(); - std::ofstream lockFile(LOCK_FILE); + outfile.close(); + std::ofstream lockFile(LOCK_FILE); lockFile.close(); - } else { // If the file already exists - means this is the second process + } + else { + // If the file already exists - means this is the second process portNumber = PORT1; peerPort = PORT2; } @@ -134,21 +169,17 @@ void communication::initializeState(int& portNumber, int& peerPort) } // Set up a socket for listening -int communication::setupSocket(int portNumber, int& sockFd, struct sockaddr_in& address) +int communication::setupSocket(int portNumber, int &sockFd, struct sockaddr_in &address) { int opt = 1; int addrlen = sizeof(address); - - sockFd = socket(AF_INET, SOCK_STREAM, 0); + sockFd = socketInterface->socket(AF_INET, SOCK_STREAM, 0); if (sockFd < 0) { - perror("Socket creation error"); return -1; } - int sockopt = setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); - if (sockopt) { - perror("setsockopt"); - close(sockFd); + int sockopt = socketInterface->setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); + if (sockopt < 0) { return -1; } @@ -157,55 +188,47 @@ int communication::setupSocket(int portNumber, int& sockFd, struct sockaddr_in& address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(portNumber); - int bindAns = bind(sockFd, (struct sockaddr*)&address, sizeof(address)); + int bindAns = socketInterface->bind(sockFd, (struct sockaddr *)&address, sizeof(address)); if (bindAns < 0) { - perror("Bind failed"); - close(sockFd); return -1; } - int listenAns = listen(sockFd, BACKLOG); + int listenAns = socketInterface->listen(sockFd, BACKLOG); if (listenAns < 0) { - perror("Listen"); - close(sockFd); return -1; } - std::cout << "Listening on port " << portNumber << std::endl; return 0; } // Wait for an incoming connection -int communication::waitForConnection(int sockFd, struct sockaddr_in& address) +int communication::waitForConnection(int sockFd, struct sockaddr_in &address) { int newSocket; int addrlen = sizeof(address); - newSocket = accept(sockFd, (struct sockaddr*)&address, (socklen_t*)&addrlen); + newSocket = socketInterface->accept(sockFd, (struct sockaddr *)&address, (socklen_t *)&addrlen); if (newSocket < 0) { - perror("Accept failed"); return -1; } return newSocket; } // Connect to a peer socket -int communication::connectToPeer(int portNumber, struct sockaddr_in& peerAddr) +int communication::connectToPeer(int portNumber, struct sockaddr_in &peerAddr) { - int clientSock = socket(AF_INET, SOCK_STREAM, 0); + int clientSock = socketInterface->socket(AF_INET, SOCK_STREAM, 0); if (clientSock < 0) { - perror("Socket creation error"); return -1; } int inetAns = inet_pton(AF_INET, IP_ADDRESS, &peerAddr.sin_addr); if (inetAns <= 0) { - perror("Invalid address/ Address not supported"); + logMessage("Client", "Server", "[ERROR] Invalid address/ Address not supported: " + std::string(strerror(errno))); return -1; } - int connectAns = connect(clientSock, (struct sockaddr*)&peerAddr, sizeof(peerAddr)); + int connectAns = socketInterface->connect(clientSock, (struct sockaddr *)&peerAddr, sizeof(peerAddr)); if (connectAns < 0) { - perror("Connection Failed"); return -1; } @@ -217,7 +240,6 @@ int communication::initConnection() { int portNumber, peerPort; initializeState(portNumber, peerPort); - int sockFd, newSocket; struct sockaddr_in address, peerAddr; @@ -229,41 +251,58 @@ int communication::initConnection() memset(&peerAddr, 0, sizeof(peerAddr)); peerAddr.sin_family = AF_INET; peerAddr.sin_port = htons(peerPort); - int clientSock = -1; std::thread recvThread; if (portNumber == PORT1) { // Wait briefly to ensure the second process (PORT2) starts listening std::this_thread::sleep_for(std::chrono::seconds(1)); - + clientSock = connectToPeer(portNumber, peerAddr); - if (clientSock < 0) return -1; + if (clientSock < 0) { + logMessage(std::to_string(portNumber), std::to_string(peerPort), "[ERROR] connect To Peer failed" + std::string(strerror(errno))); + return -1; + } + + logMessage(std::to_string(portNumber), std::to_string(peerPort), "[INFO] connected"); // Wait for an incoming connection from PORT2 newSocket = waitForConnection(sockFd, address); - if (newSocket < 0) return -1; - + if (newSocket < 0) { + logMessage(std::to_string(peerPort), std::to_string(portNumber), "[ERROR] wait For Connection failed" + std::string(strerror(errno))); + return -1; + } + + logMessage(std::to_string(peerPort), std::to_string(portNumber), "[INFO] connected"); + // Start a separate thread to handle incoming messages on the new socket recvThread = std::thread(&communication::receiveMessages, this, newSocket); - - } else { + } + else { // Wait for an incoming connection from PORT1 newSocket = waitForConnection(sockFd, address); - if (newSocket < 0) return -1; - + if (newSocket < 0) { + logMessage(std::to_string(peerPort), std::to_string(portNumber), "[ERROR] wait For Connection failed" + std::string(strerror(errno))); + return -1; + } + + logMessage(std::to_string(peerPort), std::to_string(portNumber), "[INFO] connected"); // Start a separate thread to handle incoming messages on the new socket recvThread = std::thread(&communication::receiveMessages, this, newSocket); std::this_thread::sleep_for(std::chrono::seconds(1)); // Waiting for the server to listen clientSock = connectToPeer(portNumber, peerAddr); - if (clientSock < 0) return -1; + if (clientSock < 0) { + logMessage(std::to_string(portNumber), std::to_string(peerPort), "[ERROR] wait For Connection failed" + std::string(strerror(errno))); + return -1; + } + + logMessage(std::to_string(portNumber), std::to_string(peerPort), "[INFO] connected"); // Clean up: remove state and lock files as they are no longer needed std::remove(STATE_FILE); std::remove(LOCK_FILE); } - recvThread.detach(); return clientSock; -} \ No newline at end of file +} diff --git a/src/communication.h b/src/communication.h new file mode 100644 index 00000000..40b0ff60 --- /dev/null +++ b/src/communication.h @@ -0,0 +1,50 @@ +#ifndef COMMUNICATION_H +#define COMMUNICATION_H + +#include +#include +#include +#include +#include +#include +#include "ISocket.h" + +enum class AckType { + ACK, + NACK +}; + +class communication { +public: + communication(ISocket* socketInterface); + + virtual int initConnection(); + virtual void sendMessage(int socketFd, void* data, size_t dataLen); + virtual void receiveMessages(int socketFd); + virtual void setDataHandler(std::function&)> callback); + virtual void setAckCallback(std::function callback); + static constexpr size_t PACKET_SIZE = 16; + friend class CommunicationTest; + + virtual int setupSocket(int portNumber, int& sockFd, struct sockaddr_in& address); + virtual int waitForConnection(int sockFd, struct sockaddr_in& address); + virtual int connectToPeer(int portNumber, struct sockaddr_in& peerAddr); + virtual void initializeState(int& portNumber, int& peerPort); + virtual void sendAck(int socketFd, AckType ackType); + static void logMessage(const std::string& src, const std::string& dst, const std::string& message); + static std::string getLogFileName(); + + const int PORT1 = 8080; + const int PORT2 = 8081; + static const char* STATE_FILE; + static const char* LOCK_FILE; + static std::mutex state_file_mutex; + std::vector dataReceived; + std::function&)> dataHandler; + std::function ackCallback; + +private: + ISocket* socketInterface; +}; + +#endif // COMMUNICATION_H diff --git a/test/testCommunication.cpp b/test/testCommunication.cpp new file mode 100644 index 00000000..25ccefa3 --- /dev/null +++ b/test/testCommunication.cpp @@ -0,0 +1,350 @@ +#include "communication.h" +#include "Packet.h" +#include "../sockets/mockSocket.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IP_ADDRESS "127.0.0.1" +#define PORT_NUMBER 8080 +#define PEER_PORT 8081 +// Test fixture class +class CommunicationTest : public ::testing::Test { +protected: + MockSocket mockSocket; + communication comm{&mockSocket}; + int portNumber = 8080; + int sockFd; + struct sockaddr_in address; + std::vector receivedData; + + void SetUp() override { + // Set up default behavior for mock methods + ON_CALL(mockSocket, socket(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(3)); + ON_CALL(mockSocket, setsockopt(::testing::_, ::testing::_, ::testing::_, ::testing::NotNull(), ::testing::_)) + .WillByDefault(::testing::Return(0)); + ON_CALL(mockSocket, bind(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(0)); + ON_CALL(mockSocket, listen(::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(0)); + } + + void TearDown() override { + // Clean up state and lock files + std::remove(communication::STATE_FILE); + std::remove(communication::LOCK_FILE); + } + + void receiveMessagesCallback(const std::vector& data) { + receivedData = data; + } + + void ackCallback(AckType ackType) { + // Handle acknowledgment callback if needed + } +}; + +// Test when state file does not exist +TEST_F(CommunicationTest, InitializeState_FileDoesNotExist) { + std::remove(communication::STATE_FILE); + std::remove(communication::LOCK_FILE); + + int portNumber, peerPort; + comm.initializeState(portNumber, peerPort); + + std::ifstream stateFile(communication::STATE_FILE); + std::ifstream lockFile(communication::LOCK_FILE); + + EXPECT_TRUE(stateFile.is_open()); + EXPECT_TRUE(lockFile.is_open()); + EXPECT_EQ(portNumber, PEER_PORT); + EXPECT_EQ(peerPort, PORT_NUMBER); +} + +// Test when state file already exists +TEST_F(CommunicationTest, InitializeState_FileExists) { + std::ofstream stateFile(communication::STATE_FILE); + stateFile << "initialized"; + stateFile.close(); + + std::ofstream lockFile(communication::LOCK_FILE); + lockFile.close(); + + int portNumber, peerPort; + comm.initializeState(portNumber, peerPort); + + EXPECT_EQ(portNumber, PORT_NUMBER); + EXPECT_EQ(peerPort, PEER_PORT); +} + +// Test successful setup +TEST_F(CommunicationTest, SetupSocket_Success) { + EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(mockSocket, setsockopt(3, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, ::testing::NotNull(), sizeof(int))) + .WillOnce(::testing::Return(0)); + EXPECT_CALL(mockSocket, bind(3, ::testing::NotNull(), sizeof(address))) + .WillOnce(::testing::Return(0)); + EXPECT_CALL(mockSocket, listen(3, 3)) + .WillOnce(::testing::Return(0)); + + int result = comm.setupSocket(portNumber, sockFd, address); + + EXPECT_EQ(result, 0); + EXPECT_EQ(sockFd, 3); +} + +// Test failed socket creation +TEST_F(CommunicationTest, SetupSocket_FailedSocketCreation) { + EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(::testing::Return(-1)); + + int result = comm.setupSocket(portNumber, sockFd, address); + + EXPECT_EQ(result, -1); +} + +// Test failed setsockopt +TEST_F(CommunicationTest, SetupSocket_FailedSetsockopt) { + EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(mockSocket, setsockopt(3, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, ::testing::NotNull(), sizeof(int))) + .WillOnce(::testing::Return(-1)); + + int result = comm.setupSocket(portNumber, sockFd, address); + + EXPECT_EQ(result, -1); +} + +// Test failed bind +TEST_F(CommunicationTest, SetupSocket_FailedBind) { + EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(mockSocket, setsockopt(3, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, ::testing::NotNull(), sizeof(int))) + .WillOnce(::testing::Return(0)); + EXPECT_CALL(mockSocket, bind(3, ::testing::NotNull(), sizeof(address))) + .WillOnce(::testing::Return(-1)); + + int result = comm.setupSocket(portNumber, sockFd, address); + + EXPECT_EQ(result, -1); +} + +// Test failed listen +TEST_F(CommunicationTest, SetupSocket_FailedListen) { + EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(::testing::Return(3)); + EXPECT_CALL(mockSocket, setsockopt(3, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, ::testing::NotNull(), sizeof(int))) + .WillOnce(::testing::Return(0)); + EXPECT_CALL(mockSocket, bind(3, ::testing::NotNull(), sizeof(address))) + .WillOnce(::testing::Return(0)); + EXPECT_CALL(mockSocket, listen(3, 3)) + .WillOnce(::testing::Return(-1)); + + int result = comm.setupSocket(portNumber, sockFd, address); + + EXPECT_EQ(result, -1); +} + +// Test sending messages +TEST_F(CommunicationTest, SendMessage) { + EXPECT_CALL(mockSocket, send(::testing::_, ::testing::NotNull(), ::testing::_, 0)) + .WillOnce(::testing::Return(0)); + EXPECT_CALL(mockSocket, recv(::testing::_, ::testing::NotNull(), ::testing::_, 0)) + .WillOnce(::testing::Return(0)); + + std::vector data = {1, 2, 3, 4, 5}; + comm.setAckCallback([this](AckType ackType) { + EXPECT_EQ(ackType, AckType::ACK); + + }); + + comm.sendMessage(sockFd, data.data(), data.size()); +} + +// Test sending empty data +TEST_F(CommunicationTest, SendMessages_EmptyData) { + EXPECT_CALL(mockSocket, send(::testing::_, ::testing::NotNull(), ::testing::_, 0)) + .Times(0); + EXPECT_CALL(mockSocket, recv(::testing::_, ::testing::NotNull(), ::testing::_, 0)) + .Times(0); + + + std::vector data; + comm.setAckCallback([this](AckType ackType) { + EXPECT_EQ(ackType, AckType::NACK); + }); + + comm.sendMessage(sockFd, data.data(), data.size()); +} + +// Test sending data with multiple packets +TEST_F(CommunicationTest, SendMessages_MultiplePackets) { + EXPECT_CALL(mockSocket, send(::testing::_, ::testing::NotNull(), ::testing::_, 0)) + .WillRepeatedly(::testing::Return(0)); + EXPECT_CALL(mockSocket, recv(::testing::_, ::testing::NotNull(), ::testing::_, 0)) + .WillOnce(::testing::Return(0)); + + std::vector data(200, 1); // Data size larger than packet size + comm.setAckCallback([this](AckType ackType) { + EXPECT_EQ(ackType, AckType::ACK); + }); + + comm.sendMessage(sockFd, data.data(), data.size()); +} + +// Test for `setDataReceivedCallback` +TEST_F(CommunicationTest, SetDataReceivedCallback) { + // Define a callback to test + auto callback = [this](const std::vector& data) { + EXPECT_EQ(data, std::vector({1, 2, 3, 4, 5})); + }; + + comm.setDataHandler(callback); + + // Simulate receiving data + std::vector data = {1, 2, 3, 4, 5}; + comm.dataHandler(data); // Invoke the callback directly +} + +// Test for `waitForConnection` with successful connection +TEST_F(CommunicationTest, WaitForConnection_Success) { + EXPECT_CALL(mockSocket, accept(::testing::_, ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(5)); + + struct sockaddr_in address; + int newSocket = comm.waitForConnection(sockFd, address); + + EXPECT_EQ(newSocket, 5); +} + +// Test for `initConnection` with successful initialization +TEST_F(CommunicationTest, InitConnection_Success) { + EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillRepeatedly(::testing::Return(6)); + EXPECT_CALL(mockSocket, connect(6, ::testing::NotNull(), sizeof(sockaddr_in))) + .WillOnce(::testing::Return(0)); + EXPECT_CALL(mockSocket, accept(::testing::_, ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(7)); + + + // Set up the necessary files + std::ofstream stateFile(communication::STATE_FILE); + stateFile << "initialized"; + stateFile.close(); + + std::ofstream lockFile(communication::LOCK_FILE); + lockFile.close(); + + int clientSock = comm.initConnection(); + + EXPECT_NE(clientSock, -1); +} + +// Test for `sendAck` with ACK +TEST_F(CommunicationTest, SendAck_ACK) { + EXPECT_CALL(mockSocket, send(::testing::_, ::testing::NotNull(), ::testing::_, 0)) + .WillOnce(::testing::Return(0)); + + comm.sendAck(sockFd, AckType::ACK); +} + +// Test for `sendAck` with NACK +TEST_F(CommunicationTest, SendAck_NACK) { + EXPECT_CALL(mockSocket, send(::testing::_, ::testing::NotNull(), ::testing::_, 0)) + .WillOnce(::testing::Return(0)); + + comm.sendAck(sockFd, AckType::NACK); +} + +// Test `logMessage` +TEST_F(CommunicationTest, LogToFile) { + const char* filename = "log.txt"; + std::ofstream logFile(filename); + if (!logFile.is_open()) { + FAIL() << "Failed to open log file"; + } + + // Redirect std::clog to log file + std::streambuf* orig_clog_buf = std::clog.rdbuf(); + std::clog.rdbuf(logFile.rdbuf()); + + // Generate a log message + std::clog << "Test log message" << std::endl; + + // Restore original std::clog buffer + std::clog.rdbuf(orig_clog_buf); + + logFile.close(); + + // Read back the log file and verify its contents + std::ifstream inputFile(filename); + ASSERT_TRUE(inputFile.is_open()); + + std::string logContent((std::istreambuf_iterator(inputFile)), std::istreambuf_iterator()); + inputFile.close(); + + ASSERT_TRUE(logContent.find("Test log message") != std::string::npos) << "Log content: " << logContent; + + std::remove(filename); +} + +// Test for `ConectToPeer` with FailedSocketCreation +TEST_F(CommunicationTest, ConnectToPeer_FailedSocketCreation) { + EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(::testing::Return(-1)); + + struct sockaddr_in peerAddr; + peerAddr.sin_family = AF_INET; + peerAddr.sin_port = htons(PEER_PORT); + peerAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS); + + int result = comm.connectToPeer(PEER_PORT, peerAddr); + + EXPECT_EQ(result, -1); +} + +// Test for `ConectToPeer` with FailedConnect +TEST_F(CommunicationTest, ConnectToPeer_FailedConnect) { + EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(::testing::Return(6)); + EXPECT_CALL(mockSocket, connect(6, ::testing::NotNull(), sizeof(sockaddr_in))) + .WillOnce(::testing::Return(-1)); + + struct sockaddr_in peerAddr; + peerAddr.sin_family = AF_INET; + peerAddr.sin_port = htons(PEER_PORT); + peerAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS); + + int result = comm.connectToPeer(PORT_NUMBER, peerAddr); + + EXPECT_EQ(result, -1); +} + +// Test for `ConectToPeer` with success +TEST_F(CommunicationTest, ConnectToPeer_Success) { + EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(::testing::Return(6)); + + EXPECT_CALL(mockSocket, connect(6, ::testing::NotNull(), sizeof(sockaddr_in))) + .WillOnce(::testing::Return(0)); + + struct sockaddr_in peerAddr; + peerAddr.sin_family = AF_INET; + peerAddr.sin_port = htons(PEER_PORT); + int result = comm.connectToPeer(PORT_NUMBER, peerAddr); + + EXPECT_EQ(result, 6); +} \ No newline at end of file From 31821ecc24e256e8f9b0d2436c8ac8f8bba8fc53 Mon Sep 17 00:00:00 2001 From: MalkySubotzky Date: Thu, 15 Aug 2024 17:47:59 +0300 Subject: [PATCH 05/18] Create BusManager and ServerClient communication classes for bus system management --- communication/include/bus_manager.h | 33 ++ communication/include/client.h | 54 +++ communication/include/communication.h | 48 +++ communication/include/message.h | 39 ++ communication/include/packet.h | 41 ++ communication/include/server.h | 65 ++++ .../sockets/Isocket.h | 4 +- .../sockets/mock_socket.h | 4 +- communication/sockets/real_socket.h | 125 +++++++ communication/src/bus_manager.cpp | 43 +++ communication/src/client.cpp | 95 +++++ communication/src/communication.cpp | 113 ++++++ communication/src/message.cpp | 57 +++ communication/src/packet.cpp | 32 ++ communication/src/server.cpp | 173 +++++++++ sockets/realSocket.h | 107 ------ src/Packet.cpp | 36 -- src/Packet.h | 19 - src/communication.cpp | 308 --------------- src/communication.h | 50 --- test/testCommunication.cpp | 350 ------------------ tests/client_test.cpp | 73 ++++ tests/server_test.cpp | 44 +++ 23 files changed, 1039 insertions(+), 874 deletions(-) create mode 100644 communication/include/bus_manager.h create mode 100644 communication/include/client.h create mode 100644 communication/include/communication.h create mode 100644 communication/include/message.h create mode 100644 communication/include/packet.h create mode 100644 communication/include/server.h rename sockets/ISocket.h => communication/sockets/Isocket.h (95%) rename sockets/mockSocket.h => communication/sockets/mock_socket.h (95%) create mode 100644 communication/sockets/real_socket.h create mode 100644 communication/src/bus_manager.cpp create mode 100644 communication/src/client.cpp create mode 100644 communication/src/communication.cpp create mode 100644 communication/src/message.cpp create mode 100644 communication/src/packet.cpp create mode 100644 communication/src/server.cpp delete mode 100644 sockets/realSocket.h delete mode 100644 src/Packet.cpp delete mode 100644 src/Packet.h delete mode 100644 src/communication.cpp delete mode 100644 src/communication.h delete mode 100644 test/testCommunication.cpp create mode 100644 tests/client_test.cpp create mode 100644 tests/server_test.cpp diff --git a/communication/include/bus_manager.h b/communication/include/bus_manager.h new file mode 100644 index 00000000..ed35258e --- /dev/null +++ b/communication/include/bus_manager.h @@ -0,0 +1,33 @@ +#pragma once +#include +#include +#include "server.h" +#include + +class BusManager +{ +private: + + Server server; + + // Sending according to broadcast variable + int sendToClients(const Packet &packet); + +public: + // constructor + BusManager(); + + // Sends to the server to listen for requests + int startConnection(); + + // Receives the packet that arrived and checks it before sending it out + void receiveData(void *data); + + // Implementation according to the conflict management of the CAN bus protocol + Packet checkCollision(Packet ¤tPacket); + + // Implement a priority check according to the CAN bus + Packet packetPriority(Packet &a, Packet &b); + + ~BusManager(); +}; \ No newline at end of file diff --git a/communication/include/client.h b/communication/include/client.h new file mode 100644 index 00000000..f23a74be --- /dev/null +++ b/communication/include/client.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include "message.h" +#include "../sockets/Isocket.h" +#include "../sockets/mock_socket.h" +#include "../sockets/real_socket.h" +#include + +#define PORT 8080 +#define IP "127.0.0.1" + +class Client +{ +private: + uint32_t id; + int clientSocket; + sockaddr_in servAddress; + bool connected; + std::function passPacketCom; + ISocket* socketInterface; + std::thread receiveThread; + +public: + // Constructor + Client(uint32_t id, std::function callback, ISocket* socketInterface = new RealSocket()); + + // Requesting a connection to the server + int connectToServer(); + + // Sends the packet to the manager-sync + int sendPacket(Packet &packet); + + // Waits for a message and forwards it to Communication + void receivePacket(); + + // Closes the connection + void closeConnection(); + + //For testing + int getClientSocket(); + + int isConnected(); + + bool isReceiveThreadRunning(); + + //Destructor + ~Client(); +}; + diff --git a/communication/include/communication.h b/communication/include/communication.h new file mode 100644 index 00000000..c87639d8 --- /dev/null +++ b/communication/include/communication.h @@ -0,0 +1,48 @@ +#pragma once +#include +#include "client.h" + +class Communication +{ +private: + Client client; + std::unordered_map receivedMessages; + void (*passData)(void *); + uint32_t id; +public: + // Constructor + Communication(uint32_t id, void (*passDataCallback)(void *)); + + // Sends the client to connect to server + void startConnection(); + + // Sends a message to manager + int sendMessage(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, bool isBroadcast); + + // Sends a message to manager - Async + void sendMessageAsync(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, std::function passSend, bool isBroadcast); + + // Accepts the packet from the client and checks.. + void receivePacket(Packet &p); + + // Checks if the packet is intended for him + bool checkDestId(Packet &p); + + // Checks if the Packet is currect + bool validCRC(Packet &p); + + // Receives the packet and adds it to the message + void handlePacket(Packet &p); + + // Implement error handling according to CAN bus + void handleError(); + + // Implement arrival confirmation according to the CAN bus + Packet hadArrived(); + + // Adding the packet to the complete message + void addPacketToMessage(Packet &p); + + //Destructor + ~Communication(); +}; \ No newline at end of file diff --git a/communication/include/message.h b/communication/include/message.h new file mode 100644 index 00000000..4769b831 --- /dev/null +++ b/communication/include/message.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "packet.h" + +class Message +{ +private: + std::vector packets; + uint32_t tps; + +public: + + // Default + Message() = default; + + // Constructor for sending message + Message(uint32_t srcID, void *data, int dlc, bool isBroadcast, uint32_t destID = 0xFFFF); + + // Constructor for receiving message + Message(uint32_t tps); + + // Add a packet to the received message + bool addPacket(const Packet &p); + + // Check if the message is complete + bool isComplete() const; + + // Get the complete data of the message + void *completeData() const; + + // Get the packets of the message + std::vector &getPackets(); +}; \ No newline at end of file diff --git a/communication/include/packet.h b/communication/include/packet.h new file mode 100644 index 00000000..be6251d5 --- /dev/null +++ b/communication/include/packet.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +struct Packet +{ + // Packet header containing various metadata fields + struct Header + { + uint32_t ID; // Message ID + uint32_t PSN; // Packet Sequence Number + uint32_t TPS; // Total Packet Sum + uint32_t SrcID; // Source ID + uint32_t DestID; // Destination ID + int DLC; // Data Length Code (0-8 bits) + uint16_t CRC; // Cyclic Redundancy Check for error detection + int timestamp; // Timestamp field + bool isBroadcast; // True for broadcast, false for unicas + bool passive; + bool RTR; + } header; + + uint8_t data[8]; + + // Default constructor for Packet. + Packet() = default; + + // Constructor for sending message + Packet(uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, uint8_t *data, int dlc, bool isBroadcast, bool RTR = false, bool passive=false); + + // Constructor for receiving message + Packet(uint32_t id); + + // Calculate CRC for the given data and length + uint16_t calculateCRC(const uint8_t *data, size_t length); +}; diff --git a/communication/include/server.h b/communication/include/server.h new file mode 100644 index 00000000..35c6aa85 --- /dev/null +++ b/communication/include/server.h @@ -0,0 +1,65 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include "message.h" +#include "../sockets/Isocket.h" +#include "../sockets/real_socket.h" + +class Server +{ +private: + int serverSocket; + sockaddr_in address; + int port; + bool running; + std::thread mainThread; + std::vector clientThreads; + std::vector sockets; + std::mutex socketMutex; + std::function receiveDataCallback; + std::map clientIDMap; + std::mutex IDMapMutex; + ISocket* socketInterface; + + // Starts listening for connection requests + void startThread(); + + // Implementation according to the CAN BUS + bool isValidId(uint32_t id); + + // Runs in a thread for each process - waits for a message and forwards it to the manager + void handleClient(int clientSocket); + + // Returns the sockets ID + int getClientSocketByID(uint32_t destID); + +public: + // Constructor + Server(int port, std::function callback, ISocket* socketInterface = new RealSocket()); + + // Initializes the listening socket + int startConnection(); + + // Closes the sockets and the threads + void stopServer(); + + // Sends the message to all connected processes - broadcast + int sendBroadcast(const Packet &packet); + + // Sends the message to destination + int sendDestination(const Packet &packet); + + // For testing + int getServerSocket(); + + int isRunning(); + + // Destructor + ~Server(); +}; \ No newline at end of file diff --git a/sockets/ISocket.h b/communication/sockets/Isocket.h similarity index 95% rename from sockets/ISocket.h rename to communication/sockets/Isocket.h index e086bf99..9c70b3ca 100644 --- a/sockets/ISocket.h +++ b/communication/sockets/Isocket.h @@ -1,4 +1,3 @@ - #ifndef ISOCKET_H #define ISOCKET_H @@ -15,6 +14,7 @@ class ISocket { virtual ssize_t send(int sockfd, const void *buf, size_t len, int flags) = 0; virtual ssize_t recv(int sockfd, void *buf, size_t len, int flags) = 0; virtual int close(int fd) = 0; + virtual ~ISocket() = default; }; -#endif // ISOCKET_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/sockets/mockSocket.h b/communication/sockets/mock_socket.h similarity index 95% rename from sockets/mockSocket.h rename to communication/sockets/mock_socket.h index d2596ff6..36bced05 100644 --- a/sockets/mockSocket.h +++ b/communication/sockets/mock_socket.h @@ -3,7 +3,7 @@ #define MOCKSOCKET_H #include "gmock/gmock.h" -#include "ISocket.h" +#include "Isocket.h" class MockSocket : public ISocket { public: @@ -18,4 +18,4 @@ class MockSocket : public ISocket { MOCK_METHOD(int, close, (int fd), (override)); }; -#endif // MOCKSOCKET_H \ No newline at end of file +#endif \ No newline at end of file diff --git a/communication/sockets/real_socket.h b/communication/sockets/real_socket.h new file mode 100644 index 00000000..b60a7558 --- /dev/null +++ b/communication/sockets/real_socket.h @@ -0,0 +1,125 @@ +#ifndef REALSOCKET_H +#define REALSOCKET_H + +#include "Isocket.h" +#include +#include +#include "../../logger/logger.h" +#include "../include/packet.h" + +class RealSocket : public ISocket +{ +private: + logger log; + +public: + + RealSocket() + { + log = logger("communication"); + } + + int socket(int domain, int type, int protocol) override + { + int sockFd = ::socket(domain, type, protocol); + if (sockFd < 0) { + log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Socket creation error: " + std::string(strerror(errno))); + } + + log.logMessage(logger::LogLevel::INFO, "src", "dest", "create a client socket: " + std::to_string(sockFd) + std::string(strerror(errno))); + return sockFd; + } + + int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) override + { + int sockopt = ::setsockopt(sockfd, level, optname, optval, optlen); + if (sockopt) { + log.logMessage(logger::LogLevel::ERROR, "src", "dest", "setsockopt failed: " + std::string(strerror(errno))); + close(sockfd); + } + + log.logMessage(logger::LogLevel::INFO, "src", "dest", "create a server socket: "+std::to_string(sockfd)); + return sockopt; + } + + int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) override + { + int bindAns = ::bind(sockfd, addr, addrlen); + if (bindAns < 0) { + log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Bind failed: " + std::string(strerror(errno))); + close(sockfd); + } + + return bindAns; + } + + int listen(int sockfd, int backlog) override + { + int listenAns = ::listen(sockfd, backlog); + if (listenAns < 0) { + log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Listen failed: " + std::string(strerror(errno))); + close(sockfd); + } + + log.logMessage(logger::LogLevel::INFO, "src", "dest", "server running on port " + std::to_string(8080)); + return listenAns; + } + + int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) override + { + int newSocket = ::accept(sockfd, addr, addrlen); + if (newSocket < 0) { + log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Accept failed: " + std::string(strerror(errno))); + } + + log.logMessage(logger::LogLevel::INFO, "src", "dest", "connection succeed to client socket number: " + std::to_string(sockfd)); + return newSocket; + } + + int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) override + { + int connectAns = ::connect(sockfd, addr, addrlen); + if (connectAns < 0) { + log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Connection Failed: " + std::string(strerror(errno))); + } + + log.logMessage(logger::LogLevel::INFO, "src", "dest", "connection succeed to server socket" + std::string(strerror(errno))); + return connectAns; + } + + ssize_t recv(int sockfd, void *buf, size_t len, int flags) override + { + int valread = ::recv(sockfd, buf, len, flags); + void *data = &buf; + Packet *p = static_cast(data); + + if (valread < 0) + log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Error occurred: " + std::string(strerror(errno)) + "in socket" + std::to_string(sockfd)); + else if( valread == 0) + log.logMessage(logger::LogLevel::INFO, "src", "dest", " connection closed: " + std::string(strerror(errno)) + "in socket" + std::to_string(sockfd)); + else + log.logMessage(logger::LogLevel::INFO, "src", "dest", "received packet" + *p->data); + return valread; + } + + ssize_t send(int sockfd, const void *buf, size_t len, int flags) override + { + int sendAns = ::send(sockfd, buf, len, flags); + void *data = &buf; + Packet *p = static_cast(data); + if(sendAns < 0) { + log.logMessage(logger::LogLevel::ERROR, "src", "dest", "sending packet failed: " + *p->data + std::string(strerror(errno))); + } + + log.logMessage(logger::LogLevel::INFO, "src", "dest", "sending packet succeed: " + *p->data + std::string(strerror(errno))); + return sendAns; + } + + int close(int fd) override + { + log.logMessage(logger::LogLevel::INFO, "src", "dest", "close client socket number: " + std::to_string(fd)); + return ::close(fd); + } +}; + +#endif \ No newline at end of file diff --git a/communication/src/bus_manager.cpp b/communication/src/bus_manager.cpp new file mode 100644 index 00000000..10a6b0a1 --- /dev/null +++ b/communication/src/bus_manager.cpp @@ -0,0 +1,43 @@ +#include "../include/bus_manager.h" + +// constructor +BusManager::BusManager() : server(8080, std::bind(&BusManager::receiveData, this, std::placeholders::_1)) {} + +// Sends to the server to listen for requests +int BusManager::startConnection() +{ + return server.startConnection(); +} + +// Receives the packet that arrived and checks it before sending it out +void BusManager::receiveData(void *data) +{ + Packet *p = static_cast(data); + int res = sendToClients(*p); + // Checking the case of collision and priority in functions : checkCollision,packetPriority + // Packet* resolvedPacket = checkCollision(*p); + // if (resolvedPacket) + // server.sendToClients(*resolvedPacket); +} + +// Sending according to broadcast variable +int BusManager::sendToClients(const Packet &packet) +{ + if(packet.header.isBroadcast) + return server.sendBroadcast(packet); + return server.sendDestination(packet); +} + +// Implement according to the conflict management of the CAN bus protocol +Packet BusManager::checkCollision(Packet ¤tPacket) +{ + return currentPacket; +} + +// Implement a priority check according to the CAN bus +Packet BusManager::packetPriority(Packet &a, Packet &b) +{ + return (a.header.SrcID < b.header.SrcID) ? a : b; +} + +BusManager::~BusManager(){} \ No newline at end of file diff --git a/communication/src/client.cpp b/communication/src/client.cpp new file mode 100644 index 00000000..62505245 --- /dev/null +++ b/communication/src/client.cpp @@ -0,0 +1,95 @@ +#include "../include/client.h" + +// Constructor +Client::Client(uint32_t id, std::function callback, ISocket* socketInterface) + : id(id), passPacketCom(callback), connected(false), socketInterface(socketInterface) {} + +// Requesting a connection to the server +int Client::connectToServer() +{ + clientSocket = socketInterface->socket(AF_INET, SOCK_STREAM, 0); + if (clientSocket < 0) { + return -1; + } + + servAddress.sin_family = AF_INET; + servAddress.sin_port = htons(PORT); + inet_pton(AF_INET, IP, &servAddress.sin_addr); + + int connectRes = socketInterface->connect(clientSocket, (struct sockaddr *)&servAddress, sizeof(servAddress)); + if (connectRes < 0) { + return -1; + } + + Packet packet(id); + ssize_t bytesSent = socketInterface->send(clientSocket, &packet, sizeof(Packet), 0); + if (bytesSent < sizeof(Packet)) { + return -1; + } + + connected = true; + receiveThread = std::thread(&Client::receivePacket, this); + receiveThread.detach(); + return 0; +} + +// Sends the packet to the manager-sync +int Client::sendPacket(Packet &packet) +{ + //If send executed before start + if (!connected) + return -1; + + ssize_t bytesSent = socketInterface->send(clientSocket, &packet, sizeof(Packet), 0); + if (bytesSent < sizeof(Packet)) { + return -1; + } + + return 0; +} + +// Waits for a message and forwards it to Communication +void Client::receivePacket() +{ + Packet packet; + while (connected) { + int valread = socketInterface->recv(clientSocket, &packet, sizeof(Packet), 0); + if (valread <= 0) { + break; + } + passPacketCom(packet); + } +} + +// Closes the connection +void Client::closeConnection() +{ + //implement - Notify the manager about disconnection + connected = false; + socketInterface->close(clientSocket); + if(receiveThread.joinable()) + receiveThread.join(); +} + +//For testing +int Client::getClientSocket() +{ + return clientSocket; +} + +int Client::isConnected() +{ + return connected; +} + +bool Client::isReceiveThreadRunning() +{ + return false; +} + +//Destructor +Client::~Client() +{ + closeConnection(); + delete socketInterface; +} \ No newline at end of file diff --git a/communication/src/communication.cpp b/communication/src/communication.cpp new file mode 100644 index 00000000..0175f950 --- /dev/null +++ b/communication/src/communication.cpp @@ -0,0 +1,113 @@ +#include "../include/communication.h" +#include + +// Constructor +Communication::Communication(uint32_t id, void (*passDataCallback)(void *)) : client(id, std::bind(&Communication::receivePacket, this, std::placeholders::_1)), passData(passDataCallback), id(id) {} + +// Sends the client to connect to server +void Communication::startConnection() +{ + client.connectToServer(); +} + +// Sends a message sync +int Communication::sendMessage(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, bool isBroadcast) +{ + // Creating a message and dividing it into packets + Message msg(srcID, data, dataSize, isBroadcast, destID); + + for (auto &packet : msg.getPackets()) { + int res = client.sendPacket(packet); + if(res < 0) + return res; + } + + return 0; +} + +// Sends a message Async +void Communication::sendMessageAsync(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, std::function sendCallback, bool isBroadcast) +{ + std::promise resultPromise; + std::future resultFuture = resultPromise.get_future(); + + std::thread([this, data, dataSize, destID, srcID, isBroadcast, &resultPromise]() { + int res = this->sendMessage(data, dataSize, destID, srcID, isBroadcast); + resultPromise.set_value(res); + }).detach(); + + int res = resultFuture.get(); + sendCallback(res); +} + +// Accepts the packet from the client and checks.. +void Communication::receivePacket(Packet &p) +{ + if (checkDestId(p)) { + if (validCRC(p)) + handlePacket(p); + else + handleError(); + } +} + +// Checks if the packet is intended for him +bool Communication::checkDestId(Packet &p) +{ + return p.header.isBroadcast || p.header.DestID == this->id; +} + +// Checks if the data is currect +bool Communication::validCRC(Packet &p) +{ + return p.header.CRC == p.calculateCRC(p.data, p.header.DLC); +} + +// Receives the packet and adds it to the message +void Communication::handlePacket(Packet &p) +{ + // Send acknowledgment according to CAN bus + // client.sendPacket(hadArrived()); + addPacketToMessage(p); +} + +// Implement error handling according to CAN bus +void Communication::handleError() +{ + // Handle error cases according to CAN bus +} + +// Implement arrival confirmation according to the CAN bus +Packet Communication::hadArrived() +{ + Packet ack; + // Construct and return acknowledgment packet + return ack; +} + +// Adding the packet to the complete message +void Communication::addPacketToMessage(Packet &p) +{ + // messageId may have to change according to the CAN bus + std::string messageId = std::to_string(p.header.SrcID) + "-" + std::to_string(p.header.DestID); + + // If the message already exists, we will add the packet + if (receivedMessages.find(messageId) != receivedMessages.end()) { + receivedMessages[messageId].addPacket(p); + } else { + // If the message does not exist, we will create a new message + Message msg(p.header.TPS); + msg.addPacket(p); + receivedMessages[messageId] = msg; + } + + // If the message is complete, we pass the data to the passData function + if (receivedMessages[messageId].isComplete()) { + void *completeData = receivedMessages[messageId].completeData(); + passData(completeData); + receivedMessages.erase(messageId); // Removing the message once completed + } +} + +//Destructor +Communication::~Communication(){} \ No newline at end of file diff --git a/communication/src/message.cpp b/communication/src/message.cpp new file mode 100644 index 00000000..fbd1f01e --- /dev/null +++ b/communication/src/message.cpp @@ -0,0 +1,57 @@ +#include "../include/message.h" + +// Constructor for sending message +Message::Message(uint32_t srcID, void *data, int dlc, bool isBroadcast, uint32_t destID) +{ + size_t size = dlc; + uint32_t tps = (size + 7) / 8; // Calculate the number of packets needed + for (uint32_t i = 0; i < tps; ++i) { + uint8_t packetData[8]; + size_t copySize = std::min(size - i * 8, (size_t)8); // Determine how much data to copy for each packet + std::memcpy(packetData, (uint8_t *)data + i * 8, copySize); + packets.emplace_back(i, tps, srcID, destID, packetData, copySize, isBroadcast, false, false); + } +} + +// Constructor for receiving message +Message::Message(uint32_t tps) +{ + this->tps = tps; +} + +// Add a packet to the received message +bool Message::addPacket(const Packet &p) +{ + // Implementation according to the CAN BUS + + // For further testing + // if (p.header.PSN >= packets.size()) { + // return false; + // } + + packets.push_back(p); + return true; +} + +// Check if the message is complete +bool Message::isComplete() const +{ + return packets.size() == tps; +} + +// Get the complete data of the message +void *Message::completeData() const +{ + size_t totalSize = (tps - 1) * 8 + packets.back().header.DLC; + void *data = malloc(totalSize); + for (const auto &packet : packets) { + std::memcpy((uint8_t *)data + packet.header.PSN * 8, packet.data, packet.header.DLC); + } + return data; +} + +// Get the packets of the message +std::vector &Message::getPackets() +{ + return packets; +} diff --git a/communication/src/packet.cpp b/communication/src/packet.cpp new file mode 100644 index 00000000..675d4283 --- /dev/null +++ b/communication/src/packet.cpp @@ -0,0 +1,32 @@ +#include "../include/packet.h" +// Constructor to initialize Packet for sending +Packet::Packet(uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, uint8_t *data, int dlc, bool isBroadcast, bool RTR, bool passive) +{ + header.PSN = psn; + header.TPS = tps; + header.SrcID = srcID; + header.DestID = destID; + header.DLC = dlc; + std::memcpy(this->data, data, dlc); + header.CRC = calculateCRC(data, dlc); + header.timestamp = std::time(nullptr); + header.RTR = RTR; + header.passive = passive; + header.isBroadcast = isBroadcast; +} + +// Constructor to initialize receiving Packet ID for init +Packet::Packet(uint32_t id) +{ + std::memset(&header, 0, sizeof(header)); // Initialize all fields to zero + header.SrcID = id; + header.timestamp = std::time(nullptr); +} + +// Implementation according to the CAN BUS +uint16_t Packet::calculateCRC(const uint8_t *data, size_t length) +{ + uint16_t crc = 0xFFFF; + // Calculate CRC for the given data and length + return crc; +} \ No newline at end of file diff --git a/communication/src/server.cpp b/communication/src/server.cpp new file mode 100644 index 00000000..52821034 --- /dev/null +++ b/communication/src/server.cpp @@ -0,0 +1,173 @@ +#include "../include/server.h" + +// Constructor +Server::Server(int port, std::function callback, ISocket *socketInterface) + : port(port), receiveDataCallback(callback), running(false), socketInterface(socketInterface) {} + +// Initializes the listening socket +int Server::startConnection() +{ + // Create socket TCP + serverSocket = socketInterface->socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket < 0) { + return -1; + } + + // Setting the socket to allow reuse of address and port + int opt = 1; + int setSockOptRes = socketInterface->setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); + if (setSockOptRes) { + socketInterface->close(serverSocket); + return -1; + } + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + + int bindRes = socketInterface->bind(serverSocket, (struct sockaddr *)&address, sizeof(address)); + if (bindRes < 0) { + socketInterface->close(serverSocket); + } + + int lisRes = socketInterface->listen(serverSocket, 5); + if (lisRes < 0) { + socketInterface->close(serverSocket); + return -1; + } + + running = true; + mainThread = std::thread(&Server::startThread, this); + mainThread.detach(); + return 0; +} + +// Starts listening for connection requests +void Server::startThread() +{ + while (running) { + int clientSocket = socketInterface->accept(serverSocket, nullptr, nullptr); + if (clientSocket < 0) { + return; + } + + // Opens a new thread for handleClient - listening to messages from the process + clientThreads.emplace_back(&Server::handleClient, this, clientSocket); + } +} + +// Implementation according to the CAN BUS +bool Server::isValidId(uint32_t id) +{ + return true; +} + +// Closes the sockets and the threads +void Server::stopServer() +{ + running = false; + socketInterface->close(serverSocket); + + std::lock_guard lock(socketMutex); + for (int sock : sockets) + socketInterface->close(sock); + + for (auto &th : clientThreads) + if (th.joinable()) + th.join(); + + if(mainThread.joinable()) + mainThread.join(); +} + +// Runs in a thread for each process - waits for a message and forwards it to the manager +void Server::handleClient(int clientSocket) +{ + Packet packet; + int valread = socketInterface->recv(clientSocket, &packet, sizeof(Packet), 0); + if (valread <= 0) + return; + + uint32_t clientID = packet.header.SrcID; + if(!isValidId(clientID)) + return; + { + std::lock_guard lock(IDMapMutex); + clientIDMap[clientSocket] = clientID; + } + + { + std::lock_guard lock(socketMutex); + sockets.push_back(clientSocket); + } + + while (true) { + int valread = socketInterface->recv(clientSocket, &packet, sizeof(Packet), 0); + if (valread <= 0) + break; + + receiveDataCallback(&packet); + } + + // If the process is no longer connected + std::lock_guard lock(socketMutex); + auto it = std::find(sockets.begin(), sockets.end(), clientSocket); + if (it != sockets.end()) + sockets.erase(it); +} + +// Returns the sockets ID +int Server::getClientSocketByID(uint32_t destID) +{ + std::lock_guard lock(IDMapMutex); + for (const auto &client : clientIDMap) + if (client.second == destID) + return client.first; + + return -1; +} + +// Sends the message to destination +int Server::sendDestination(const Packet &packet) +{ + int targetSocket = getClientSocketByID(packet.header.DestID); + if (targetSocket == -1) + return -1; + + ssize_t bytesSent = socketInterface->send(targetSocket, &packet, sizeof(Packet), 0); + if (bytesSent < sizeof(Packet)) + return -1; + + return 0; +} + +// Sends the message to all connected processes - broadcast +int Server::sendBroadcast(const Packet &packet) +{ + std::lock_guard lock(socketMutex); + for (int sock : sockets) { + ssize_t bytesSent = socketInterface->send(sock, &packet, sizeof(Packet), 0); + if (bytesSent < sizeof(Packet)) + return -1; + } + + return 0; +} + +// For testing +int Server::getServerSocket() +{ + return serverSocket; +} + +int Server::isRunning() +{ + return running; +} + +// Destructor +Server::~Server() +{ + stopServer(); + delete socketInterface; +} diff --git a/sockets/realSocket.h b/sockets/realSocket.h deleted file mode 100644 index d5c39bd4..00000000 --- a/sockets/realSocket.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef REALSOCKET_H -#define REALSOCKET_H - -#include "ISocket.h" -#include -#include - -class RealSocket : public ISocket -{ -public: - int socket(int domain, int type, int protocol) override - { - int sockFd = ::socket(domain, type, protocol); - if (sockFd < 0) - { - communication::logMessage("Server", "Client", "[ERROR] Socket creation error: " + std::string(strerror(errno))); - } - - return sockFd; - } - - int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) override - { - int sockopt = ::setsockopt(sockfd, level, optname, optval, optlen); - if (sockopt) - { - communication::logMessage("Server", "Client", "[ERROR] setsockopt failed: " + std::string(strerror(errno))); - close(sockfd); - } - - return sockopt; - } - - int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) override - { - int bindAns = ::bind(sockfd, addr, addrlen); - if (bindAns < 0) - { - communication::logMessage("Server", "Client", "[ERROR] Bind failed: " + std::string(strerror(errno))); - close(sockfd); - } - - return bindAns; - } - - int listen(int sockfd, int backlog) override - { - int listenAns = ::listen(sockfd, backlog); - if (listenAns < 0) - { - communication::logMessage("Server", "Client", "[ERROR] Listen failed: " + std::string(strerror(errno))); - close(sockfd); - } - - return listenAns; - } - - int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) override - { - int newSocket = ::accept(sockfd, addr, addrlen); - if (newSocket < 0) - { - communication::logMessage("Server", "Client", "[ERROR] Accept failed: " + std::string(strerror(errno))); - } - - return newSocket; - } - - int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) override - { - int connectAns = ::connect(sockfd, addr, addrlen); - if (connectAns < 0) - { - communication::logMessage("Client", "Server", "[ERROR] Connection Failed: " + std::string(strerror(errno))); - } - - return connectAns; - } - - ssize_t recv(int sockfd, void *buf, size_t len, int flags) override - { - int valread = ::recv(sockfd, buf, len, flags); - if (valread <= 0) - { - communication::logMessage("Server", "Client", "[ERROR] Connection closed or error occurred: " + std::string(strerror(errno))); - } - - return valread; - } - - ssize_t send(int sockfd, const void *buf, size_t len, int flags) override - { - int sendAns = ::send(sockfd, buf, len, flags); - if(sendAns < 0){ - communication::logMessage("Server", "Client", "[ERROR] Failed to send acknowledgment: " + std::string(strerror(errno))); - } - - return sendAns; - } - - int close(int fd) override - { - return ::close(fd); - } -}; - -#endif // REALSOCKET_H \ No newline at end of file diff --git a/src/Packet.cpp b/src/Packet.cpp deleted file mode 100644 index c7d3d549..00000000 --- a/src/Packet.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "Packet.h" - -int Packet::getPsn() const -{ - return PSN; -} - -int Packet::getTotalPacketSum() const -{ - return totalPacketSum; -} - -uint8_t* Packet::getData() -{ - return data; -} - -void Packet::setPsn(int psn) -{ - this->PSN = psn; -} - -void Packet::setTotalPacketSum(int totalPacketSum) -{ - this->totalPacketSum = totalPacketSum; -} - -void Packet::setData(const void* inputData, size_t size) -{ - std::memcpy(this->data, inputData, size); -} - -void Packet::resetData() -{ - memset(data, '\0', communication::PACKET_SIZE); -} diff --git a/src/Packet.h b/src/Packet.h deleted file mode 100644 index 8b89f42f..00000000 --- a/src/Packet.h +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include -#include "communication.h" - -class Packet { -public: - int getPsn() const; - int getTotalPacketSum() const; - uint8_t* getData(); - void setPsn(int psn); - void setTotalPacketSum(int totalPacketSum); - void setData(const void* inputData, size_t size); - void resetData(); - -private: - int PSN = 0; - int totalPacketSum = 0; - uint8_t data[communication::PACKET_SIZE] = {0}; -}; diff --git a/src/communication.cpp b/src/communication.cpp deleted file mode 100644 index a36011e1..00000000 --- a/src/communication.cpp +++ /dev/null @@ -1,308 +0,0 @@ -#include "communication.h" -#include "Packet.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define IP_ADDRESS "127.0.0.1" -#define BACKLOG 3 - -// Constants for packet size and state file names -const char *communication::STATE_FILE = "../../comm_state3.txt"; -const char *communication::LOCK_FILE = "../../comm_state3.lock"; -std::mutex communication::state_file_mutex; - -// Function to generate a timestamped log file name -std::string communication::getLogFileName() -{ - auto now = std::chrono::system_clock::now(); - auto time = std::chrono::system_clock::to_time_t(now); - std::tm tm = *std::localtime(&time); - - std::ostringstream oss; - oss << "../../" << std::put_time(&tm, "%Y_%m_%d_%H_%M_%S") << "_communication.log"; - return oss.str(); -} - -communication::communication(ISocket *socketInterface) : socketInterface(socketInterface) {} - -void communication::logMessage(const std::string &src, const std::string &dst, const std::string &message) -{ - std::string logFileName = getLogFileName(); - std::ofstream logFile(logFileName, std::ios_base::app); - if (!logFile) { - std::cerr << "[ERROR] Failed to open log file" << std::endl; - return; - } - - auto now = std::chrono::system_clock::now(); - auto time = std::chrono::system_clock::to_time_t(now); - std::tm tm = *std::localtime(&time); - logFile << "[" << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << "] " - << "SRC: " << src << " DST: " << dst << " " << message << std::endl; -} - -// Send acknowledgment messages -void communication::sendAck(int socketFd, AckType ackType) -{ - const char *ackMessage = (ackType == AckType::ACK) ? "ACK" : "NACK"; - socketInterface->send(socketFd, ackMessage, strlen(ackMessage), 0); -} - -// Set the callback for data received -void communication::setDataHandler(std::function &)> callback) -{ - dataHandler = callback; -} - -// Set the callback for acknowledgment received -void communication::setAckCallback(std::function callback) -{ - ackCallback = callback; -} - -// Receive messages from a socket -void communication::receiveMessages(int socketFd) -{ - Packet packet; - std::vector receivedData; - while (true) { - int valread = socketInterface->recv(socketFd, &packet, sizeof(Packet), 0); - std::string receivePacketForLog = ""; - - // Append the received data to the vector - for (int i = 0; i < PACKET_SIZE; ++i) { - receivedData.push_back(packet.getData()[i]); - receivePacketForLog += packet.getData()[i]; - } - logMessage("Server", "Client", "[INFO] " + receivePacketForLog); - - // Notify the user via callback function - if (dataHandler) { - dataHandler(receivedData); - } - - receivedData.clear(); - - // Check if all packets have been received - if (packet.getPsn() == packet.getTotalPacketSum()) { - sendAck(socketFd, AckType::ACK); - } - } -} - -// Send message in a socket -void communication::sendMessage(int socketFd, void *data, size_t dataLen) -{ - if (data == nullptr || dataLen == 0) { - if (ackCallback) { - ackCallback(AckType::NACK); - } - return; - } - - Packet packet; - int psn = 0; - size_t offset = 0; - size_t totalPackets = (dataLen + PACKET_SIZE - 1) / PACKET_SIZE; - - while (offset < dataLen) { - size_t packetLen = std::min(dataLen - offset, PACKET_SIZE); - packet.setData(static_cast(data) + offset, packetLen); - packet.setPsn(psn++); - packet.setTotalPacketSum(totalPackets - 1); - - int sendAns = socketInterface->send(socketFd, &packet, sizeof(Packet), 0); - if (sendAns < 0) { - break; - } - - offset += packetLen; - packet.resetData(); - } - // Wait for acknowledgment - char buffer[4]; - int valread = socketInterface->recv(socketFd, buffer, sizeof(buffer), 0); - if (valread > 0) { - buffer[valread] = '\0'; // Null-terminate the received string - AckType receivedAck = (strcmp(buffer, "ACK") == 0) ? AckType::ACK : AckType::NACK; - logMessage("Client", "Server", "[INFO] " + (std::string)buffer); - // Trigger the acknowledgment callback - if (ackCallback) { - ackCallback(receivedAck); - } - } -} - -// Initialize the state based on the presence of the state file -void communication::initializeState(int &portNumber, int &peerPort) -{ - std::lock_guard lock(state_file_mutex); - std::ifstream infile(STATE_FILE); - - if (!infile) { - // If the file doesn't exist - means this is the first process - portNumber = PORT2; - peerPort = PORT1; - std::ofstream outfile(STATE_FILE); - outfile << "initialized"; - outfile.close(); - std::ofstream lockFile(LOCK_FILE); - lockFile.close(); - } - else { - // If the file already exists - means this is the second process - portNumber = PORT1; - peerPort = PORT2; - } - infile.close(); -} - -// Set up a socket for listening -int communication::setupSocket(int portNumber, int &sockFd, struct sockaddr_in &address) -{ - int opt = 1; - int addrlen = sizeof(address); - sockFd = socketInterface->socket(AF_INET, SOCK_STREAM, 0); - if (sockFd < 0) { - return -1; - } - - int sockopt = socketInterface->setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); - if (sockopt < 0) { - return -1; - } - - memset(&address, 0, sizeof(address)); - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(portNumber); - - int bindAns = socketInterface->bind(sockFd, (struct sockaddr *)&address, sizeof(address)); - if (bindAns < 0) { - return -1; - } - - int listenAns = socketInterface->listen(sockFd, BACKLOG); - if (listenAns < 0) { - return -1; - } - std::cout << "Listening on port " << portNumber << std::endl; - return 0; -} - -// Wait for an incoming connection -int communication::waitForConnection(int sockFd, struct sockaddr_in &address) -{ - int newSocket; - int addrlen = sizeof(address); - newSocket = socketInterface->accept(sockFd, (struct sockaddr *)&address, (socklen_t *)&addrlen); - if (newSocket < 0) { - return -1; - } - return newSocket; -} - -// Connect to a peer socket -int communication::connectToPeer(int portNumber, struct sockaddr_in &peerAddr) -{ - int clientSock = socketInterface->socket(AF_INET, SOCK_STREAM, 0); - if (clientSock < 0) { - return -1; - } - - int inetAns = inet_pton(AF_INET, IP_ADDRESS, &peerAddr.sin_addr); - if (inetAns <= 0) { - logMessage("Client", "Server", "[ERROR] Invalid address/ Address not supported: " + std::string(strerror(errno))); - return -1; - } - - int connectAns = socketInterface->connect(clientSock, (struct sockaddr *)&peerAddr, sizeof(peerAddr)); - if (connectAns < 0) { - return -1; - } - - return clientSock; -} - -// Initialize the connection -int communication::initConnection() -{ - int portNumber, peerPort; - initializeState(portNumber, peerPort); - int sockFd, newSocket; - struct sockaddr_in address, peerAddr; - - int setupSocketAns = setupSocket(portNumber, sockFd, address); - if (setupSocketAns < 0) { - return -1; - } - - memset(&peerAddr, 0, sizeof(peerAddr)); - peerAddr.sin_family = AF_INET; - peerAddr.sin_port = htons(peerPort); - int clientSock = -1; - std::thread recvThread; - - if (portNumber == PORT1) { - // Wait briefly to ensure the second process (PORT2) starts listening - std::this_thread::sleep_for(std::chrono::seconds(1)); - - clientSock = connectToPeer(portNumber, peerAddr); - if (clientSock < 0) { - logMessage(std::to_string(portNumber), std::to_string(peerPort), "[ERROR] connect To Peer failed" + std::string(strerror(errno))); - return -1; - } - - logMessage(std::to_string(portNumber), std::to_string(peerPort), "[INFO] connected"); - - // Wait for an incoming connection from PORT2 - newSocket = waitForConnection(sockFd, address); - if (newSocket < 0) { - logMessage(std::to_string(peerPort), std::to_string(portNumber), "[ERROR] wait For Connection failed" + std::string(strerror(errno))); - return -1; - } - - logMessage(std::to_string(peerPort), std::to_string(portNumber), "[INFO] connected"); - - // Start a separate thread to handle incoming messages on the new socket - recvThread = std::thread(&communication::receiveMessages, this, newSocket); - } - else { - // Wait for an incoming connection from PORT1 - newSocket = waitForConnection(sockFd, address); - if (newSocket < 0) { - logMessage(std::to_string(peerPort), std::to_string(portNumber), "[ERROR] wait For Connection failed" + std::string(strerror(errno))); - return -1; - } - - logMessage(std::to_string(peerPort), std::to_string(portNumber), "[INFO] connected"); - // Start a separate thread to handle incoming messages on the new socket - recvThread = std::thread(&communication::receiveMessages, this, newSocket); - std::this_thread::sleep_for(std::chrono::seconds(1)); // Waiting for the server to listen - - clientSock = connectToPeer(portNumber, peerAddr); - if (clientSock < 0) { - logMessage(std::to_string(portNumber), std::to_string(peerPort), "[ERROR] wait For Connection failed" + std::string(strerror(errno))); - return -1; - } - - logMessage(std::to_string(portNumber), std::to_string(peerPort), "[INFO] connected"); - - // Clean up: remove state and lock files as they are no longer needed - std::remove(STATE_FILE); - std::remove(LOCK_FILE); - } - recvThread.detach(); - return clientSock; -} diff --git a/src/communication.h b/src/communication.h deleted file mode 100644 index 40b0ff60..00000000 --- a/src/communication.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef COMMUNICATION_H -#define COMMUNICATION_H - -#include -#include -#include -#include -#include -#include -#include "ISocket.h" - -enum class AckType { - ACK, - NACK -}; - -class communication { -public: - communication(ISocket* socketInterface); - - virtual int initConnection(); - virtual void sendMessage(int socketFd, void* data, size_t dataLen); - virtual void receiveMessages(int socketFd); - virtual void setDataHandler(std::function&)> callback); - virtual void setAckCallback(std::function callback); - static constexpr size_t PACKET_SIZE = 16; - friend class CommunicationTest; - - virtual int setupSocket(int portNumber, int& sockFd, struct sockaddr_in& address); - virtual int waitForConnection(int sockFd, struct sockaddr_in& address); - virtual int connectToPeer(int portNumber, struct sockaddr_in& peerAddr); - virtual void initializeState(int& portNumber, int& peerPort); - virtual void sendAck(int socketFd, AckType ackType); - static void logMessage(const std::string& src, const std::string& dst, const std::string& message); - static std::string getLogFileName(); - - const int PORT1 = 8080; - const int PORT2 = 8081; - static const char* STATE_FILE; - static const char* LOCK_FILE; - static std::mutex state_file_mutex; - std::vector dataReceived; - std::function&)> dataHandler; - std::function ackCallback; - -private: - ISocket* socketInterface; -}; - -#endif // COMMUNICATION_H diff --git a/test/testCommunication.cpp b/test/testCommunication.cpp deleted file mode 100644 index 25ccefa3..00000000 --- a/test/testCommunication.cpp +++ /dev/null @@ -1,350 +0,0 @@ -#include "communication.h" -#include "Packet.h" -#include "../sockets/mockSocket.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define IP_ADDRESS "127.0.0.1" -#define PORT_NUMBER 8080 -#define PEER_PORT 8081 -// Test fixture class -class CommunicationTest : public ::testing::Test { -protected: - MockSocket mockSocket; - communication comm{&mockSocket}; - int portNumber = 8080; - int sockFd; - struct sockaddr_in address; - std::vector receivedData; - - void SetUp() override { - // Set up default behavior for mock methods - ON_CALL(mockSocket, socket(::testing::_, ::testing::_, ::testing::_)) - .WillByDefault(::testing::Return(3)); - ON_CALL(mockSocket, setsockopt(::testing::_, ::testing::_, ::testing::_, ::testing::NotNull(), ::testing::_)) - .WillByDefault(::testing::Return(0)); - ON_CALL(mockSocket, bind(::testing::_, ::testing::_, ::testing::_)) - .WillByDefault(::testing::Return(0)); - ON_CALL(mockSocket, listen(::testing::_, ::testing::_)) - .WillByDefault(::testing::Return(0)); - } - - void TearDown() override { - // Clean up state and lock files - std::remove(communication::STATE_FILE); - std::remove(communication::LOCK_FILE); - } - - void receiveMessagesCallback(const std::vector& data) { - receivedData = data; - } - - void ackCallback(AckType ackType) { - // Handle acknowledgment callback if needed - } -}; - -// Test when state file does not exist -TEST_F(CommunicationTest, InitializeState_FileDoesNotExist) { - std::remove(communication::STATE_FILE); - std::remove(communication::LOCK_FILE); - - int portNumber, peerPort; - comm.initializeState(portNumber, peerPort); - - std::ifstream stateFile(communication::STATE_FILE); - std::ifstream lockFile(communication::LOCK_FILE); - - EXPECT_TRUE(stateFile.is_open()); - EXPECT_TRUE(lockFile.is_open()); - EXPECT_EQ(portNumber, PEER_PORT); - EXPECT_EQ(peerPort, PORT_NUMBER); -} - -// Test when state file already exists -TEST_F(CommunicationTest, InitializeState_FileExists) { - std::ofstream stateFile(communication::STATE_FILE); - stateFile << "initialized"; - stateFile.close(); - - std::ofstream lockFile(communication::LOCK_FILE); - lockFile.close(); - - int portNumber, peerPort; - comm.initializeState(portNumber, peerPort); - - EXPECT_EQ(portNumber, PORT_NUMBER); - EXPECT_EQ(peerPort, PEER_PORT); -} - -// Test successful setup -TEST_F(CommunicationTest, SetupSocket_Success) { - EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) - .WillOnce(::testing::Return(3)); - EXPECT_CALL(mockSocket, setsockopt(3, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, ::testing::NotNull(), sizeof(int))) - .WillOnce(::testing::Return(0)); - EXPECT_CALL(mockSocket, bind(3, ::testing::NotNull(), sizeof(address))) - .WillOnce(::testing::Return(0)); - EXPECT_CALL(mockSocket, listen(3, 3)) - .WillOnce(::testing::Return(0)); - - int result = comm.setupSocket(portNumber, sockFd, address); - - EXPECT_EQ(result, 0); - EXPECT_EQ(sockFd, 3); -} - -// Test failed socket creation -TEST_F(CommunicationTest, SetupSocket_FailedSocketCreation) { - EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) - .WillOnce(::testing::Return(-1)); - - int result = comm.setupSocket(portNumber, sockFd, address); - - EXPECT_EQ(result, -1); -} - -// Test failed setsockopt -TEST_F(CommunicationTest, SetupSocket_FailedSetsockopt) { - EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) - .WillOnce(::testing::Return(3)); - EXPECT_CALL(mockSocket, setsockopt(3, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, ::testing::NotNull(), sizeof(int))) - .WillOnce(::testing::Return(-1)); - - int result = comm.setupSocket(portNumber, sockFd, address); - - EXPECT_EQ(result, -1); -} - -// Test failed bind -TEST_F(CommunicationTest, SetupSocket_FailedBind) { - EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) - .WillOnce(::testing::Return(3)); - EXPECT_CALL(mockSocket, setsockopt(3, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, ::testing::NotNull(), sizeof(int))) - .WillOnce(::testing::Return(0)); - EXPECT_CALL(mockSocket, bind(3, ::testing::NotNull(), sizeof(address))) - .WillOnce(::testing::Return(-1)); - - int result = comm.setupSocket(portNumber, sockFd, address); - - EXPECT_EQ(result, -1); -} - -// Test failed listen -TEST_F(CommunicationTest, SetupSocket_FailedListen) { - EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) - .WillOnce(::testing::Return(3)); - EXPECT_CALL(mockSocket, setsockopt(3, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, ::testing::NotNull(), sizeof(int))) - .WillOnce(::testing::Return(0)); - EXPECT_CALL(mockSocket, bind(3, ::testing::NotNull(), sizeof(address))) - .WillOnce(::testing::Return(0)); - EXPECT_CALL(mockSocket, listen(3, 3)) - .WillOnce(::testing::Return(-1)); - - int result = comm.setupSocket(portNumber, sockFd, address); - - EXPECT_EQ(result, -1); -} - -// Test sending messages -TEST_F(CommunicationTest, SendMessage) { - EXPECT_CALL(mockSocket, send(::testing::_, ::testing::NotNull(), ::testing::_, 0)) - .WillOnce(::testing::Return(0)); - EXPECT_CALL(mockSocket, recv(::testing::_, ::testing::NotNull(), ::testing::_, 0)) - .WillOnce(::testing::Return(0)); - - std::vector data = {1, 2, 3, 4, 5}; - comm.setAckCallback([this](AckType ackType) { - EXPECT_EQ(ackType, AckType::ACK); - - }); - - comm.sendMessage(sockFd, data.data(), data.size()); -} - -// Test sending empty data -TEST_F(CommunicationTest, SendMessages_EmptyData) { - EXPECT_CALL(mockSocket, send(::testing::_, ::testing::NotNull(), ::testing::_, 0)) - .Times(0); - EXPECT_CALL(mockSocket, recv(::testing::_, ::testing::NotNull(), ::testing::_, 0)) - .Times(0); - - - std::vector data; - comm.setAckCallback([this](AckType ackType) { - EXPECT_EQ(ackType, AckType::NACK); - }); - - comm.sendMessage(sockFd, data.data(), data.size()); -} - -// Test sending data with multiple packets -TEST_F(CommunicationTest, SendMessages_MultiplePackets) { - EXPECT_CALL(mockSocket, send(::testing::_, ::testing::NotNull(), ::testing::_, 0)) - .WillRepeatedly(::testing::Return(0)); - EXPECT_CALL(mockSocket, recv(::testing::_, ::testing::NotNull(), ::testing::_, 0)) - .WillOnce(::testing::Return(0)); - - std::vector data(200, 1); // Data size larger than packet size - comm.setAckCallback([this](AckType ackType) { - EXPECT_EQ(ackType, AckType::ACK); - }); - - comm.sendMessage(sockFd, data.data(), data.size()); -} - -// Test for `setDataReceivedCallback` -TEST_F(CommunicationTest, SetDataReceivedCallback) { - // Define a callback to test - auto callback = [this](const std::vector& data) { - EXPECT_EQ(data, std::vector({1, 2, 3, 4, 5})); - }; - - comm.setDataHandler(callback); - - // Simulate receiving data - std::vector data = {1, 2, 3, 4, 5}; - comm.dataHandler(data); // Invoke the callback directly -} - -// Test for `waitForConnection` with successful connection -TEST_F(CommunicationTest, WaitForConnection_Success) { - EXPECT_CALL(mockSocket, accept(::testing::_, ::testing::NotNull(), ::testing::_)) - .WillOnce(::testing::Return(5)); - - struct sockaddr_in address; - int newSocket = comm.waitForConnection(sockFd, address); - - EXPECT_EQ(newSocket, 5); -} - -// Test for `initConnection` with successful initialization -TEST_F(CommunicationTest, InitConnection_Success) { - EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) - .WillRepeatedly(::testing::Return(6)); - EXPECT_CALL(mockSocket, connect(6, ::testing::NotNull(), sizeof(sockaddr_in))) - .WillOnce(::testing::Return(0)); - EXPECT_CALL(mockSocket, accept(::testing::_, ::testing::NotNull(), ::testing::_)) - .WillOnce(::testing::Return(7)); - - - // Set up the necessary files - std::ofstream stateFile(communication::STATE_FILE); - stateFile << "initialized"; - stateFile.close(); - - std::ofstream lockFile(communication::LOCK_FILE); - lockFile.close(); - - int clientSock = comm.initConnection(); - - EXPECT_NE(clientSock, -1); -} - -// Test for `sendAck` with ACK -TEST_F(CommunicationTest, SendAck_ACK) { - EXPECT_CALL(mockSocket, send(::testing::_, ::testing::NotNull(), ::testing::_, 0)) - .WillOnce(::testing::Return(0)); - - comm.sendAck(sockFd, AckType::ACK); -} - -// Test for `sendAck` with NACK -TEST_F(CommunicationTest, SendAck_NACK) { - EXPECT_CALL(mockSocket, send(::testing::_, ::testing::NotNull(), ::testing::_, 0)) - .WillOnce(::testing::Return(0)); - - comm.sendAck(sockFd, AckType::NACK); -} - -// Test `logMessage` -TEST_F(CommunicationTest, LogToFile) { - const char* filename = "log.txt"; - std::ofstream logFile(filename); - if (!logFile.is_open()) { - FAIL() << "Failed to open log file"; - } - - // Redirect std::clog to log file - std::streambuf* orig_clog_buf = std::clog.rdbuf(); - std::clog.rdbuf(logFile.rdbuf()); - - // Generate a log message - std::clog << "Test log message" << std::endl; - - // Restore original std::clog buffer - std::clog.rdbuf(orig_clog_buf); - - logFile.close(); - - // Read back the log file and verify its contents - std::ifstream inputFile(filename); - ASSERT_TRUE(inputFile.is_open()); - - std::string logContent((std::istreambuf_iterator(inputFile)), std::istreambuf_iterator()); - inputFile.close(); - - ASSERT_TRUE(logContent.find("Test log message") != std::string::npos) << "Log content: " << logContent; - - std::remove(filename); -} - -// Test for `ConectToPeer` with FailedSocketCreation -TEST_F(CommunicationTest, ConnectToPeer_FailedSocketCreation) { - EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) - .WillOnce(::testing::Return(-1)); - - struct sockaddr_in peerAddr; - peerAddr.sin_family = AF_INET; - peerAddr.sin_port = htons(PEER_PORT); - peerAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS); - - int result = comm.connectToPeer(PEER_PORT, peerAddr); - - EXPECT_EQ(result, -1); -} - -// Test for `ConectToPeer` with FailedConnect -TEST_F(CommunicationTest, ConnectToPeer_FailedConnect) { - EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) - .WillOnce(::testing::Return(6)); - EXPECT_CALL(mockSocket, connect(6, ::testing::NotNull(), sizeof(sockaddr_in))) - .WillOnce(::testing::Return(-1)); - - struct sockaddr_in peerAddr; - peerAddr.sin_family = AF_INET; - peerAddr.sin_port = htons(PEER_PORT); - peerAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS); - - int result = comm.connectToPeer(PORT_NUMBER, peerAddr); - - EXPECT_EQ(result, -1); -} - -// Test for `ConectToPeer` with success -TEST_F(CommunicationTest, ConnectToPeer_Success) { - EXPECT_CALL(mockSocket, socket(AF_INET, SOCK_STREAM, 0)) - .WillOnce(::testing::Return(6)); - - EXPECT_CALL(mockSocket, connect(6, ::testing::NotNull(), sizeof(sockaddr_in))) - .WillOnce(::testing::Return(0)); - - struct sockaddr_in peerAddr; - peerAddr.sin_family = AF_INET; - peerAddr.sin_port = htons(PEER_PORT); - int result = comm.connectToPeer(PORT_NUMBER, peerAddr); - - EXPECT_EQ(result, 6); -} \ No newline at end of file diff --git a/tests/client_test.cpp b/tests/client_test.cpp new file mode 100644 index 00000000..44f5e4c7 --- /dev/null +++ b/tests/client_test.cpp @@ -0,0 +1,73 @@ + +#include +#include +#include "../src/client.h" +#include "../sockets/mock_socket.h" +#include "../src/message.h" + +using ::testing::_; +using ::testing::Return; + +class ClientTest : public ::testing::Test { +protected: + MockSocket mockSocket; + Client *client; + + void SetUp() override { + + ON_CALL(mockSocket, socket(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(1)); + ON_CALL(mockSocket, setsockopt(::testing::_, ::testing::_, ::testing::_, ::testing::NotNull(), ::testing::_)) + .WillByDefault(::testing::Return(0)); + ON_CALL(mockSocket, bind(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(0)); + ON_CALL(mockSocket, listen(::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(0)); + ON_CALL(mockSocket, connect(_, _, _)) + .WillByDefault(::testing::Return(0)); + ON_CALL(mockSocket, close(_)) + .WillByDefault(::testing::Return(0)); + Packet packet; + ON_CALL(mockSocket, send(_, _, _, _)) + .WillByDefault(::testing::Return(sizeof(Packet))); + + client= new Client(1, [](Packet&){}, &mockSocket); + } + void TearDown() override { + // delete client; + } +}; + +TEST_F(ClientTest, ConstructorCreatesSocket) { + EXPECT_GT(client->getClientSocket(), 0); +} + +TEST_F(ClientTest, ConnectToServerSuccess) { + int result = client->connectToServer(); + EXPECT_EQ(result, 0); +} + +TEST_F(ClientTest, ConnectToServerFailure) { + EXPECT_CALL(mockSocket, connect(_, _, _)) + .WillOnce(Return(-1)); + int result = client->connectToServer(); + EXPECT_EQ(result, -1); +} + +TEST_F(ClientTest, SendPacketSuccess) { + Packet packet; + client->connectToServer(); + int result = client->sendPacket(packet); + EXPECT_EQ(result, 0); +} + +TEST_F(ClientTest, SendPacketFailureNotConnected) { + Packet packet; + int result = client->sendPacket(packet); + EXPECT_EQ(result, -1); +} + +TEST_F(ClientTest, CloseConnection) { + client->closeConnection(); + EXPECT_FALSE(client->isConnected()); +} diff --git a/tests/server_test.cpp b/tests/server_test.cpp new file mode 100644 index 00000000..0beaca2c --- /dev/null +++ b/tests/server_test.cpp @@ -0,0 +1,44 @@ + +#include +#include +#include "../src/server.h" +#include "../sockets/mock_socket.h" +#include "../src/message.h" + +using ::testing::_; +using ::testing::Return; + +class ServerTest : public ::testing::Test { +protected: + MockSocket mockSocket; + Server *server; + + void SetUp() override { + + ON_CALL(mockSocket, socket(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(1)); + ON_CALL(mockSocket, setsockopt(::testing::_, ::testing::_, ::testing::_, ::testing::NotNull(), ::testing::_)) + .WillByDefault(::testing::Return(0)); + ON_CALL(mockSocket, bind(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(0)); + ON_CALL(mockSocket, listen(::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(0)); + ON_CALL(mockSocket, accept(_, _, _)) + .WillByDefault(::testing::Return(0)); + ON_CALL(mockSocket, close(_)) + .WillByDefault(::testing::Return(0)); + Packet packet; + ON_CALL(mockSocket, send(_, _, _, _)) + .WillByDefault(::testing::Return(sizeof(Packet))); + + server= new Server(1, [](void*){}, &mockSocket); + } + void TearDown() override { + // delete client; + } +}; + +TEST_F(ServerTest, ConstructorCreatesSocket) { + EXPECT_GT(server->getServerSocket(), 0); +} + From b42ce4d30307aa601af86c470ff8131bb15c69bb Mon Sep 17 00:00:00 2001 From: devora6936 Date: Tue, 3 Sep 2024 11:42:21 +0300 Subject: [PATCH 06/18] Add SIGINT signal handling for graceful system shutdown --- communication/include/bus_manager.h | 6 +++++- communication/include/client.h | 2 +- communication/include/communication.h | 6 ++++++ communication/include/server.h | 5 +++-- communication/src/bus_manager.cpp | 19 +++++++++++++++++-- communication/src/client.cpp | 1 + communication/src/communication.cpp | 21 +++++++++++++++++++-- communication/src/server.cpp | 5 +++-- 8 files changed, 55 insertions(+), 10 deletions(-) diff --git a/communication/include/bus_manager.h b/communication/include/bus_manager.h index ed35258e..53e80fd0 100644 --- a/communication/include/bus_manager.h +++ b/communication/include/bus_manager.h @@ -7,8 +7,9 @@ class BusManager { private: - Server server; + // A static variable that holds an instance of the class + static BusManager* instance; // Sending according to broadcast variable int sendToClients(const Packet &packet); @@ -29,5 +30,8 @@ class BusManager // Implement a priority check according to the CAN bus Packet packetPriority(Packet &a, Packet &b); + // Static method to handle SIGINT signal + static void signalHandler(int signum); + ~BusManager(); }; \ No newline at end of file diff --git a/communication/include/client.h b/communication/include/client.h index f23a74be..4622666c 100644 --- a/communication/include/client.h +++ b/communication/include/client.h @@ -20,7 +20,7 @@ class Client uint32_t id; int clientSocket; sockaddr_in servAddress; - bool connected; + std::atomic connected; std::function passPacketCom; ISocket* socketInterface; std::thread receiveThread; diff --git a/communication/include/communication.h b/communication/include/communication.h index c87639d8..7bd9ff1b 100644 --- a/communication/include/communication.h +++ b/communication/include/communication.h @@ -9,6 +9,9 @@ class Communication std::unordered_map receivedMessages; void (*passData)(void *); uint32_t id; + // A static variable that holds an instance of the class + static Communication* instance; + public: // Constructor Communication(uint32_t id, void (*passDataCallback)(void *)); @@ -42,6 +45,9 @@ class Communication // Adding the packet to the complete message void addPacketToMessage(Packet &p); + + // Static method to handle SIGINT signal + static void signalHandler(int signum); //Destructor ~Communication(); diff --git a/communication/include/server.h b/communication/include/server.h index 35c6aa85..7d51d59b 100644 --- a/communication/include/server.h +++ b/communication/include/server.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "message.h" #include "../sockets/Isocket.h" #include "../sockets/real_socket.h" @@ -17,7 +18,7 @@ class Server int serverSocket; sockaddr_in address; int port; - bool running; + std::atomic running; std::thread mainThread; std::vector clientThreads; std::vector sockets; @@ -62,4 +63,4 @@ class Server // Destructor ~Server(); -}; \ No newline at end of file +}; diff --git a/communication/src/bus_manager.cpp b/communication/src/bus_manager.cpp index 10a6b0a1..831ce6c0 100644 --- a/communication/src/bus_manager.cpp +++ b/communication/src/bus_manager.cpp @@ -1,7 +1,14 @@ #include "../include/bus_manager.h" +BusManager* BusManager::instance = nullptr; + // constructor -BusManager::BusManager() : server(8080, std::bind(&BusManager::receiveData, this, std::placeholders::_1)) {} +BusManager::BusManager() : server(8080, std::bind(&BusManager::receiveData, this, std::placeholders::_1)) +{ + instance = this; + // Setup the signal handler for SIGINT + signal(SIGINT, BusManager::signalHandler); +} // Sends to the server to listen for requests int BusManager::startConnection() @@ -40,4 +47,12 @@ Packet BusManager::packetPriority(Packet &a, Packet &b) return (a.header.SrcID < b.header.SrcID) ? a : b; } -BusManager::~BusManager(){} \ No newline at end of file +// Static method to handle SIGINT signal +void BusManager::signalHandler(int signum) +{ + if (instance) { + instance->server.stopServer(); // Call the stopServer method + } + exit(signum); +} +BusManager::~BusManager() {} diff --git a/communication/src/client.cpp b/communication/src/client.cpp index 62505245..881dd635 100644 --- a/communication/src/client.cpp +++ b/communication/src/client.cpp @@ -30,6 +30,7 @@ int Client::connectToServer() connected = true; receiveThread = std::thread(&Client::receivePacket, this); receiveThread.detach(); + return 0; } diff --git a/communication/src/communication.cpp b/communication/src/communication.cpp index 0175f950..201cd629 100644 --- a/communication/src/communication.cpp +++ b/communication/src/communication.cpp @@ -1,8 +1,15 @@ #include "../include/communication.h" #include +Communication* Communication::instance = nullptr; + // Constructor -Communication::Communication(uint32_t id, void (*passDataCallback)(void *)) : client(id, std::bind(&Communication::receivePacket, this, std::placeholders::_1)), passData(passDataCallback), id(id) {} +Communication::Communication(uint32_t id, void (*passDataCallback)(void *)) : client(id, std::bind(&Communication::receivePacket, this, std::placeholders::_1)), passData(passDataCallback), id(id) +{ + instance = this; + // Setup the signal handler for SIGINT + signal(SIGINT, Communication::signalHandler); +} // Sends the client to connect to server void Communication::startConnection() @@ -109,5 +116,15 @@ void Communication::addPacketToMessage(Packet &p) } } +// Static method to handle SIGINT signal +void Communication::signalHandler(int signum) +{ + std::cout << "Interrupt signal (" << signum << ") received.\n"; + if (instance) { + instance->client.closeConnection(); // Call the closeConnection method + } + exit(signum); +} + //Destructor -Communication::~Communication(){} \ No newline at end of file +Communication::~Communication() {} \ No newline at end of file diff --git a/communication/src/server.cpp b/communication/src/server.cpp index 52821034..653aa9e3 100644 --- a/communication/src/server.cpp +++ b/communication/src/server.cpp @@ -1,7 +1,7 @@ #include "../include/server.h" // Constructor -Server::Server(int port, std::function callback, ISocket *socketInterface) +Server::Server(int port, std::function callback, ISocket* socketInterface) : port(port), receiveDataCallback(callback), running(false), socketInterface(socketInterface) {} // Initializes the listening socket @@ -39,6 +39,7 @@ int Server::startConnection() running = true; mainThread = std::thread(&Server::startThread, this); mainThread.detach(); + return 0; } @@ -170,4 +171,4 @@ Server::~Server() { stopServer(); delete socketInterface; -} +} \ No newline at end of file From bc3c01731e72db4f7262d060c286a88ac32af112 Mon Sep 17 00:00:00 2001 From: devora6936 Date: Tue, 3 Sep 2024 15:37:30 +0300 Subject: [PATCH 07/18] Making the manager a singleton --- communication/include/bus_manager.h | 12 +++++++++--- communication/include/server.h | 1 + communication/src/bus_manager.cpp | 21 ++++++++++++++++++--- communication/src/communication.cpp | 4 +++- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/communication/include/bus_manager.h b/communication/include/bus_manager.h index 53e80fd0..4dec5428 100644 --- a/communication/include/bus_manager.h +++ b/communication/include/bus_manager.h @@ -8,16 +8,22 @@ class BusManager { private: Server server; - // A static variable that holds an instance of the class + + // Singleton instance static BusManager* instance; + static std::mutex managerMutex; // Sending according to broadcast variable int sendToClients(const Packet &packet); -public: - // constructor + // Private constructor BusManager(); +public: + + //Static function to return a singleton instance + static BusManager* getInstance(); + // Sends to the server to listen for requests int startConnection(); diff --git a/communication/include/server.h b/communication/include/server.h index 7d51d59b..85a6ae35 100644 --- a/communication/include/server.h +++ b/communication/include/server.h @@ -41,6 +41,7 @@ class Server int getClientSocketByID(uint32_t destID); public: + // Constructor Server(int port, std::function callback, ISocket* socketInterface = new RealSocket()); diff --git a/communication/src/bus_manager.cpp b/communication/src/bus_manager.cpp index 831ce6c0..d435e032 100644 --- a/communication/src/bus_manager.cpp +++ b/communication/src/bus_manager.cpp @@ -1,15 +1,27 @@ #include "../include/bus_manager.h" BusManager* BusManager::instance = nullptr; +std::mutex BusManager::managerMutex; -// constructor +//Private constructor BusManager::BusManager() : server(8080, std::bind(&BusManager::receiveData, this, std::placeholders::_1)) { - instance = this; // Setup the signal handler for SIGINT signal(SIGINT, BusManager::signalHandler); } +// Static function to return a singleton instance +BusManager* BusManager::getInstance() { + if (instance == nullptr) { + // Lock the mutex to prevent multiple threads from creating instances simultaneously + std::lock_guard lock(managerMutex); + if (instance == nullptr) { + instance = new BusManager(); + } + } + return instance; +} + // Sends to the server to listen for requests int BusManager::startConnection() { @@ -55,4 +67,7 @@ void BusManager::signalHandler(int signum) } exit(signum); } -BusManager::~BusManager() {} + +BusManager::~BusManager() { + instance = nullptr; +} diff --git a/communication/src/communication.cpp b/communication/src/communication.cpp index 201cd629..f8e5a2a9 100644 --- a/communication/src/communication.cpp +++ b/communication/src/communication.cpp @@ -127,4 +127,6 @@ void Communication::signalHandler(int signum) } //Destructor -Communication::~Communication() {} \ No newline at end of file +Communication::~Communication() { + instance = nullptr; +} \ No newline at end of file From 4f08d0db9a684facaba51a580331ea7c86dfbc1a Mon Sep 17 00:00:00 2001 From: MalkySubotzky Date: Sun, 8 Sep 2024 18:29:28 +0300 Subject: [PATCH 08/18] Logger version update --- .gitignore | 10 +++++-- communication/include/bus_manager.h | 4 +-- communication/include/packet.h | 2 +- communication/include/server.h | 1 + communication/sockets/real_socket.h | 43 ++++++++++++++--------------- communication/src/message.cpp | 3 +- communication/src/packet.cpp | 3 +- communication/src/server.cpp | 22 +++++++++------ 8 files changed, 50 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index 39591c23..30555a59 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ BUILD/ build/ +runFile/ # Ignore log files *.log @@ -10,7 +11,7 @@ build/ *.a *.so *.exe - +*.sh # Ignore temporary files *.tmp *.swp @@ -23,4 +24,9 @@ build/ # Ignore system-specific files .DS_Store -Thumbs.db \ No newline at end of file +Thumbs.db + +user_1.cpp +user_2.cpp +user_3.cpp +main_bus.cpp diff --git a/communication/include/bus_manager.h b/communication/include/bus_manager.h index 4dec5428..675f3978 100644 --- a/communication/include/bus_manager.h +++ b/communication/include/bus_manager.h @@ -8,11 +8,11 @@ class BusManager { private: Server server; - + // Singleton instance static BusManager* instance; static std::mutex managerMutex; - + // Sending according to broadcast variable int sendToClients(const Packet &packet); diff --git a/communication/include/packet.h b/communication/include/packet.h index be6251d5..44a0883e 100644 --- a/communication/include/packet.h +++ b/communication/include/packet.h @@ -31,7 +31,7 @@ struct Packet Packet() = default; // Constructor for sending message - Packet(uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, uint8_t *data, int dlc, bool isBroadcast, bool RTR = false, bool passive=false); + Packet(uint32_t id, uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, uint8_t *data, int dlc, bool isBroadcast, bool RTR = false, bool passive=false); // Constructor for receiving message Packet(uint32_t id); diff --git a/communication/include/server.h b/communication/include/server.h index 85a6ae35..65d0a59f 100644 --- a/communication/include/server.h +++ b/communication/include/server.h @@ -23,6 +23,7 @@ class Server std::vector clientThreads; std::vector sockets; std::mutex socketMutex; + std::mutex threadMutex; std::function receiveDataCallback; std::map clientIDMap; std::mutex IDMapMutex; diff --git a/communication/sockets/real_socket.h b/communication/sockets/real_socket.h index b60a7558..a9be3823 100644 --- a/communication/sockets/real_socket.h +++ b/communication/sockets/real_socket.h @@ -23,10 +23,9 @@ class RealSocket : public ISocket { int sockFd = ::socket(domain, type, protocol); if (sockFd < 0) { - log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Socket creation error: " + std::string(strerror(errno))); + log.logMessage(logger::LogLevel::ERROR, "socket creation error: " + std::string(strerror(errno))); } - - log.logMessage(logger::LogLevel::INFO, "src", "dest", "create a client socket: " + std::to_string(sockFd) + std::string(strerror(errno))); + log.logMessage(logger::LogLevel::INFO, "create a client socket: " + std::to_string(sockFd) + std::string(" ") + std::string(strerror(errno))); return sockFd; } @@ -34,11 +33,11 @@ class RealSocket : public ISocket { int sockopt = ::setsockopt(sockfd, level, optname, optval, optlen); if (sockopt) { - log.logMessage(logger::LogLevel::ERROR, "src", "dest", "setsockopt failed: " + std::string(strerror(errno))); + log.logMessage(logger::LogLevel::ERROR, "setsockopt failed: " + std::string(strerror(errno))); close(sockfd); } - log.logMessage(logger::LogLevel::INFO, "src", "dest", "create a server socket: "+std::to_string(sockfd)); + log.logMessage(logger::LogLevel::INFO, "create a server socket: " + std::to_string(sockfd)); return sockopt; } @@ -46,7 +45,7 @@ class RealSocket : public ISocket { int bindAns = ::bind(sockfd, addr, addrlen); if (bindAns < 0) { - log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Bind failed: " + std::string(strerror(errno))); + log.logMessage(logger::LogLevel::ERROR, "Bind failed: " + std::string(strerror(errno))); close(sockfd); } @@ -57,11 +56,11 @@ class RealSocket : public ISocket { int listenAns = ::listen(sockfd, backlog); if (listenAns < 0) { - log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Listen failed: " + std::string(strerror(errno))); + log.logMessage(logger::LogLevel::ERROR, "Listen failed: " + std::string(strerror(errno))); close(sockfd); } - log.logMessage(logger::LogLevel::INFO, "src", "dest", "server running on port " + std::to_string(8080)); + log.logMessage(logger::LogLevel::INFO, "server running on port " + std::to_string(8080)); return listenAns; } @@ -69,10 +68,10 @@ class RealSocket : public ISocket { int newSocket = ::accept(sockfd, addr, addrlen); if (newSocket < 0) { - log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Accept failed: " + std::string(strerror(errno))); + log.logMessage(logger::LogLevel::ERROR, "Accept failed: " + std::string(strerror(errno))); } - log.logMessage(logger::LogLevel::INFO, "src", "dest", "connection succeed to client socket number: " + std::to_string(sockfd)); + log.logMessage(logger::LogLevel::INFO, "connection succeed to client socket number: " + std::to_string(sockfd)); return newSocket; } @@ -80,44 +79,44 @@ class RealSocket : public ISocket { int connectAns = ::connect(sockfd, addr, addrlen); if (connectAns < 0) { - log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Connection Failed: " + std::string(strerror(errno))); + log.logMessage(logger::LogLevel::ERROR, "process", "server", "Connection Failed: " + std::string(strerror(errno))); } - log.logMessage(logger::LogLevel::INFO, "src", "dest", "connection succeed to server socket" + std::string(strerror(errno))); + log.logMessage(logger::LogLevel::INFO, "process", "server", "connection succeed: " + std::string(strerror(errno))); return connectAns; } ssize_t recv(int sockfd, void *buf, size_t len, int flags) override { int valread = ::recv(sockfd, buf, len, flags); - void *data = &buf; - Packet *p = static_cast(data); + const Packet *p = static_cast(buf); if (valread < 0) - log.logMessage(logger::LogLevel::ERROR, "src", "dest", "Error occurred: " + std::string(strerror(errno)) + "in socket" + std::to_string(sockfd)); + log.logMessage(logger::LogLevel::ERROR, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string("Error occurred: in socket ") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); else if( valread == 0) - log.logMessage(logger::LogLevel::INFO, "src", "dest", " connection closed: " + std::string(strerror(errno)) + "in socket" + std::to_string(sockfd)); + log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string(" connection closed: in socket") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); else - log.logMessage(logger::LogLevel::INFO, "src", "dest", "received packet" + *p->data); + log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string("received packet number: ") + std::to_string(p->header.PSN) + std::string(", of messageId: ") + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno))); return valread; } ssize_t send(int sockfd, const void *buf, size_t len, int flags) override { int sendAns = ::send(sockfd, buf, len, flags); - void *data = &buf; - Packet *p = static_cast(data); + const Packet *p = static_cast(buf); if(sendAns < 0) { - log.logMessage(logger::LogLevel::ERROR, "src", "dest", "sending packet failed: " + *p->data + std::string(strerror(errno))); + log.logMessage(logger::LogLevel::ERROR, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), "sending packet number: " + std::to_string(p->header.PSN) + ", of messageId: " + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno))); } - log.logMessage(logger::LogLevel::INFO, "src", "dest", "sending packet succeed: " + *p->data + std::string(strerror(errno))); + log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), "sending packet number: " + std::to_string(p->header.PSN) + ", of messageId: " + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno))); return sendAns; } int close(int fd) override { - log.logMessage(logger::LogLevel::INFO, "src", "dest", "close client socket number: " + std::to_string(fd)); + log.logMessage(logger::LogLevel::INFO, "close socket number: " + std::to_string(fd)); + log.cleanUp(); + shutdown(fd, SHUT_RDWR); return ::close(fd); } }; diff --git a/communication/src/message.cpp b/communication/src/message.cpp index fbd1f01e..d6cd1174 100644 --- a/communication/src/message.cpp +++ b/communication/src/message.cpp @@ -9,7 +9,8 @@ Message::Message(uint32_t srcID, void *data, int dlc, bool isBroadcast, uint32_t uint8_t packetData[8]; size_t copySize = std::min(size - i * 8, (size_t)8); // Determine how much data to copy for each packet std::memcpy(packetData, (uint8_t *)data + i * 8, copySize); - packets.emplace_back(i, tps, srcID, destID, packetData, copySize, isBroadcast, false, false); + uint32_t id = srcID + destID; + packets.emplace_back(id, i, tps, srcID, destID, packetData, copySize, isBroadcast, false, false); } } diff --git a/communication/src/packet.cpp b/communication/src/packet.cpp index 675d4283..907a5962 100644 --- a/communication/src/packet.cpp +++ b/communication/src/packet.cpp @@ -1,7 +1,8 @@ #include "../include/packet.h" // Constructor to initialize Packet for sending -Packet::Packet(uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, uint8_t *data, int dlc, bool isBroadcast, bool RTR, bool passive) +Packet::Packet(uint32_t id, uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, uint8_t *data, int dlc, bool isBroadcast, bool RTR, bool passive) { + header.ID = id; header.PSN = psn; header.TPS = tps; header.SrcID = srcID; diff --git a/communication/src/server.cpp b/communication/src/server.cpp index 653aa9e3..816fe221 100644 --- a/communication/src/server.cpp +++ b/communication/src/server.cpp @@ -51,9 +51,11 @@ void Server::startThread() if (clientSocket < 0) { return; } - // Opens a new thread for handleClient - listening to messages from the process - clientThreads.emplace_back(&Server::handleClient, this, clientSocket); + { + std::lock_guard lock(threadMutex); + clientThreads.emplace_back(&Server::handleClient, this, clientSocket); + } } } @@ -68,15 +70,17 @@ void Server::stopServer() { running = false; socketInterface->close(serverSocket); - - std::lock_guard lock(socketMutex); - for (int sock : sockets) - socketInterface->close(sock); - - for (auto &th : clientThreads) + { + std::lock_guard lock(socketMutex); + for (int sock : sockets) + socketInterface->close(sock); + } + { + std::lock_guard lock(threadMutex); + for (auto &th : clientThreads) if (th.joinable()) th.join(); - + } if(mainThread.joinable()) mainThread.join(); } From 5e9739d9e44860d19e903e564e2803f3d11c17c0 Mon Sep 17 00:00:00 2001 From: devora6936 Date: Mon, 9 Sep 2024 15:41:32 +0300 Subject: [PATCH 09/18] Get IDs that should connect from GUI --- communication/include/bus_manager.h | 5 +++-- communication/include/communication.h | 1 + communication/src/bus_manager.cpp | 11 +++++++---- communication/src/communication.cpp | 7 ++++++- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/communication/include/bus_manager.h b/communication/include/bus_manager.h index 675f3978..d6a33eac 100644 --- a/communication/include/bus_manager.h +++ b/communication/include/bus_manager.h @@ -12,17 +12,18 @@ class BusManager // Singleton instance static BusManager* instance; static std::mutex managerMutex; + //SyncCommunication syncCommunication; // Sending according to broadcast variable int sendToClients(const Packet &packet); // Private constructor - BusManager(); + BusManager(std::vector idShouldConnect, uint32_t limit); public: //Static function to return a singleton instance - static BusManager* getInstance(); + static BusManager* getInstance(std::vector idShouldConnect, uint32_t limit); // Sends to the server to listen for requests int startConnection(); diff --git a/communication/include/communication.h b/communication/include/communication.h index 7bd9ff1b..e151bd03 100644 --- a/communication/include/communication.h +++ b/communication/include/communication.h @@ -9,6 +9,7 @@ class Communication std::unordered_map receivedMessages; void (*passData)(void *); uint32_t id; + //SyncCommunication syncCommunication; // A static variable that holds an instance of the class static Communication* instance; diff --git a/communication/src/bus_manager.cpp b/communication/src/bus_manager.cpp index d435e032..d84098ba 100644 --- a/communication/src/bus_manager.cpp +++ b/communication/src/bus_manager.cpp @@ -4,19 +4,19 @@ BusManager* BusManager::instance = nullptr; std::mutex BusManager::managerMutex; //Private constructor -BusManager::BusManager() : server(8080, std::bind(&BusManager::receiveData, this, std::placeholders::_1)) +BusManager::BusManager(std::vector idShouldConnect, uint32_t limit) : server(8080, std::bind(&BusManager::receiveData, this, std::placeholders::_1)) //,syncCommunication(idShouldConnect, limit) { // Setup the signal handler for SIGINT signal(SIGINT, BusManager::signalHandler); } // Static function to return a singleton instance -BusManager* BusManager::getInstance() { +BusManager* BusManager::getInstance(std::vector idShouldConnect, uint32_t limit) { if (instance == nullptr) { // Lock the mutex to prevent multiple threads from creating instances simultaneously std::lock_guard lock(managerMutex); if (instance == nullptr) { - instance = new BusManager(); + instance = new BusManager(idShouldConnect, limit); } } return instance; @@ -25,7 +25,10 @@ BusManager* BusManager::getInstance() { // Sends to the server to listen for requests int BusManager::startConnection() { - return server.startConnection(); + + int isConnected = server.startConnection(); + //syncCommunication.notifyProcess() + return isConnected; } // Receives the packet that arrived and checks it before sending it out diff --git a/communication/src/communication.cpp b/communication/src/communication.cpp index f8e5a2a9..2cf7f2bb 100644 --- a/communication/src/communication.cpp +++ b/communication/src/communication.cpp @@ -14,7 +14,12 @@ Communication::Communication(uint32_t id, void (*passDataCallback)(void *)) : cl // Sends the client to connect to server void Communication::startConnection() { - client.connectToServer(); + //Waiting for manager + //syncCommunication.isManagerRunning() + int isConnected = client.connectToServer(); + //Increases the shared memory and blocks the process - if not all are connected + //syncCommunication.registerProcess() + return isConnected; } // Sends a message sync From 66db70fe6328918bed204447daccf700510b00e8 Mon Sep 17 00:00:00 2001 From: ChayaleF Date: Wed, 11 Sep 2024 12:12:03 +0300 Subject: [PATCH 10/18] Add ErrorCode enum and tests for error handling --- communication/include/bus_manager.h | 11 +- .../include/{client.h => client_connection.h} | 22 +- communication/include/communication.h | 40 +-- communication/include/message.h | 1 - communication/include/packet.h | 22 +- .../include/{server.h => server_connection.h} | 38 ++- communication/sockets/Isocket.h | 1 + communication/sockets/real_socket.cpp | 121 ++++++++ communication/sockets/real_socket.h | 116 +------- communication/src/bus_manager.cpp | 14 +- communication/src/client.cpp | 96 ------- communication/src/client_connection.cpp | 127 ++++++++ communication/src/communication.cpp | 73 +++-- communication/src/error_code.h | 46 +++ communication/src/message.cpp | 12 +- communication/src/packet.cpp | 16 +- communication/src/server.cpp | 178 ------------ communication/src/server_connection.cpp | 267 +++++++++++++++++ tests/client_test.cpp | 182 ++++++++++-- tests/server_test.cpp | 271 ++++++++++++++++-- 20 files changed, 1139 insertions(+), 515 deletions(-) rename communication/include/{client.h => client_connection.h} (63%) rename communication/include/{server.h => server_connection.h} (56%) create mode 100644 communication/sockets/real_socket.cpp delete mode 100644 communication/src/client.cpp create mode 100644 communication/src/client_connection.cpp create mode 100644 communication/src/error_code.h delete mode 100644 communication/src/server.cpp create mode 100644 communication/src/server_connection.cpp diff --git a/communication/include/bus_manager.h b/communication/include/bus_manager.h index d6a33eac..74f0beec 100644 --- a/communication/include/bus_manager.h +++ b/communication/include/bus_manager.h @@ -1,13 +1,13 @@ #pragma once #include #include -#include "server.h" +#include "server_connection.h" #include class BusManager { private: - Server server; + ServerConnection server; // Singleton instance static BusManager* instance; @@ -15,21 +15,20 @@ class BusManager //SyncCommunication syncCommunication; // Sending according to broadcast variable - int sendToClients(const Packet &packet); + ErrorCode sendToClients(const Packet &packet); // Private constructor BusManager(std::vector idShouldConnect, uint32_t limit); public: - //Static function to return a singleton instance static BusManager* getInstance(std::vector idShouldConnect, uint32_t limit); // Sends to the server to listen for requests - int startConnection(); + ErrorCode startConnection(); // Receives the packet that arrived and checks it before sending it out - void receiveData(void *data); + void receiveData(Packet &p); // Implementation according to the conflict management of the CAN bus protocol Packet checkCollision(Packet ¤tPacket); diff --git a/communication/include/client.h b/communication/include/client_connection.h similarity index 63% rename from communication/include/client.h rename to communication/include/client_connection.h index 4622666c..f72d0bf0 100644 --- a/communication/include/client.h +++ b/communication/include/client_connection.h @@ -10,14 +10,14 @@ #include "../sockets/mock_socket.h" #include "../sockets/real_socket.h" #include +#include "error_code.h" #define PORT 8080 #define IP "127.0.0.1" -class Client +class ClientConnection { private: - uint32_t id; int clientSocket; sockaddr_in servAddress; std::atomic connected; @@ -27,21 +27,27 @@ class Client public: // Constructor - Client(uint32_t id, std::function callback, ISocket* socketInterface = new RealSocket()); + ClientConnection(std::function callback, ISocket* socketInterface = new RealSocket()); // Requesting a connection to the server - int connectToServer(); + ErrorCode connectToServer(int id); // Sends the packet to the manager-sync - int sendPacket(Packet &packet); + ErrorCode sendPacket(Packet &packet); // Waits for a message and forwards it to Communication void receivePacket(); // Closes the connection - void closeConnection(); + ErrorCode closeConnection(); - //For testing + // Setter for passPacketCom + void setCallback(std::function callback); + + // Setter for socketInterface + void setSocketInterface(ISocket* socketInterface); + + // For testing int getClientSocket(); int isConnected(); @@ -49,6 +55,6 @@ class Client bool isReceiveThreadRunning(); //Destructor - ~Client(); + ~ClientConnection(); }; diff --git a/communication/include/communication.h b/communication/include/communication.h index e151bd03..5485abae 100644 --- a/communication/include/communication.h +++ b/communication/include/communication.h @@ -1,31 +1,20 @@ #pragma once #include -#include "client.h" - +#include "client_connection.h" +#include "../sockets/Isocket.h" +#include "error_code.h" class Communication { private: - Client client; + ClientConnection client; std::unordered_map receivedMessages; - void (*passData)(void *); + void (*passData)(uint32_t, void *); uint32_t id; //SyncCommunication syncCommunication; + // A static variable that holds an instance of the class static Communication* instance; -public: - // Constructor - Communication(uint32_t id, void (*passDataCallback)(void *)); - - // Sends the client to connect to server - void startConnection(); - - // Sends a message to manager - int sendMessage(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, bool isBroadcast); - - // Sends a message to manager - Async - void sendMessageAsync(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, std::function passSend, bool isBroadcast); - // Accepts the packet from the client and checks.. void receivePacket(Packet &p); @@ -49,7 +38,24 @@ class Communication // Static method to handle SIGINT signal static void signalHandler(int signum); + + void setId(uint32_t newId); + + void setPassDataCallback(void (*callback)(uint32_t, void *)); + +public: + // Constructor + Communication(uint32_t id, void (*passDataCallback)(uint32_t, void *)); + // Sends the client to connect to server + ErrorCode startConnection(); + + // Sends a message to manager + ErrorCode sendMessage(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, bool isBroadcast); + + // Sends a message to manager - Async + void sendMessageAsync(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, std::function passSend, bool isBroadcast); + //Destructor ~Communication(); }; \ No newline at end of file diff --git a/communication/include/message.h b/communication/include/message.h index 4769b831..e81ce9d8 100644 --- a/communication/include/message.h +++ b/communication/include/message.h @@ -15,7 +15,6 @@ class Message uint32_t tps; public: - // Default Message() = default; diff --git a/communication/include/packet.h b/communication/include/packet.h index 44a0883e..25391d3e 100644 --- a/communication/include/packet.h +++ b/communication/include/packet.h @@ -6,9 +6,14 @@ #include #include #include - -struct Packet +#include +#include +#include +#include +#define SIZE_PACKET 8 +class Packet { +public: // Packet header containing various metadata fields struct Header { @@ -17,7 +22,7 @@ struct Packet uint32_t TPS; // Total Packet Sum uint32_t SrcID; // Source ID uint32_t DestID; // Destination ID - int DLC; // Data Length Code (0-8 bits) + uint8_t DLC; // Data Length Code (0-8 bits) uint16_t CRC; // Cyclic Redundancy Check for error detection int timestamp; // Timestamp field bool isBroadcast; // True for broadcast, false for unicas @@ -25,17 +30,20 @@ struct Packet bool RTR; } header; - uint8_t data[8]; + void *data[SIZE_PACKET]; // Default constructor for Packet. Packet() = default; // Constructor for sending message - Packet(uint32_t id, uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, uint8_t *data, int dlc, bool isBroadcast, bool RTR = false, bool passive=false); - + Packet(uint32_t id, uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, void *data, uint8_t dlc, bool isBroadcast, bool RTR = false, bool passive = false); + // Constructor for receiving message Packet(uint32_t id); // Calculate CRC for the given data and length - uint16_t calculateCRC(const uint8_t *data, size_t length); + uint16_t calculateCRC(const void *data, size_t length); + + // A function to convert the data to hexa (logger) + std::string pointerToHex(const void *ptr, size_t size) const; }; diff --git a/communication/include/server.h b/communication/include/server_connection.h similarity index 56% rename from communication/include/server.h rename to communication/include/server_connection.h index 65d0a59f..0b8ba802 100644 --- a/communication/include/server.h +++ b/communication/include/server_connection.h @@ -11,8 +11,9 @@ #include "message.h" #include "../sockets/Isocket.h" #include "../sockets/real_socket.h" +#include "error_code.h" -class Server +class ServerConnection { private: int serverSocket; @@ -24,7 +25,7 @@ class Server std::vector sockets; std::mutex socketMutex; std::mutex threadMutex; - std::function receiveDataCallback; + std::function receiveDataCallback; std::map clientIDMap; std::mutex IDMapMutex; ISocket* socketInterface; @@ -44,25 +45,46 @@ class Server public: // Constructor - Server(int port, std::function callback, ISocket* socketInterface = new RealSocket()); + ServerConnection(int port, std::function callback, ISocket* socketInterface = new RealSocket()); // Initializes the listening socket - int startConnection(); + ErrorCode startConnection(); // Closes the sockets and the threads void stopServer(); // Sends the message to all connected processes - broadcast - int sendBroadcast(const Packet &packet); + ErrorCode sendBroadcast(const Packet &packet); + + // Sets the server's port number, throws an exception if the port is invalid. + void setPort(int port); + + // Sets the callback for receiving data, throws an exception if the callback is null. + void setReceiveDataCallback(std::function callback); + + // Sets the socket interface, throws an exception if the socketInterface is null. + void setSocketInterface(ISocket *socketInterface); // Sends the message to destination - int sendDestination(const Packet &packet); + ErrorCode sendDestination(const Packet &packet); // For testing int getServerSocket(); int isRunning(); - + + std::vector* getSockets(); + + std::mutex* getSocketMutex(); + + std::mutex* getIDMapMutex(); + + std::map* getClientIDMap(); + + void testHandleClient(int clientSocket); + + int testGetClientSocketByID(uint32_t destID); + // Destructor - ~Server(); + ~ServerConnection(); }; diff --git a/communication/sockets/Isocket.h b/communication/sockets/Isocket.h index 9c70b3ca..2ecfdd85 100644 --- a/communication/sockets/Isocket.h +++ b/communication/sockets/Isocket.h @@ -2,6 +2,7 @@ #define ISOCKET_H #include +#include "../../logger/logger.h" class ISocket { public: diff --git a/communication/sockets/real_socket.cpp b/communication/sockets/real_socket.cpp new file mode 100644 index 00000000..dda6f23f --- /dev/null +++ b/communication/sockets/real_socket.cpp @@ -0,0 +1,121 @@ +#include "real_socket.h" + +logger RealSocket::log = logger("communication"); + +RealSocket::RealSocket(){} + +int RealSocket::socket(int domain, int type, int protocol) +{ + int sockFd = ::socket(domain, type, protocol); + if (sockFd < 0) + { + RealSocket::log.logMessage(logger::LogLevel::ERROR, "socket creation error: " + std::string(strerror(errno))); + } + RealSocket::log.logMessage(logger::LogLevel::INFO, "create a client socket: " + std::to_string(sockFd) + std::string(" ") + std::string(strerror(errno))); + return sockFd; +} + +int RealSocket::setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) +{ + int sockopt = ::setsockopt(sockfd, level, optname, optval, optlen); + if (sockopt) + { + RealSocket::log.logMessage(logger::LogLevel::ERROR, "setsockopt failed: " + std::string(strerror(errno))); + close(sockfd); + } + + RealSocket::log.logMessage(logger::LogLevel::INFO, "create a server socket: " + std::to_string(sockfd)); + return sockopt; +} + +int RealSocket::bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + int bindAns = ::bind(sockfd, addr, addrlen); + if (bindAns < 0) + { + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Bind failed: " + std::string(strerror(errno))); + close(sockfd); + } + + return bindAns; +} + +int RealSocket::listen(int sockfd, int backlog) +{ + int listenAns = ::listen(sockfd, backlog); + if (listenAns < 0) + { + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Listen failed: " + std::string(strerror(errno))); + close(sockfd); + } + + RealSocket::log.logMessage(logger::LogLevel::INFO, "server running on port " + std::to_string(8080)); + return listenAns; +} + +int RealSocket::accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + int newSocket = ::accept(sockfd, addr, addrlen); + if (newSocket < 0) + { + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Accept failed: " + std::string(strerror(errno))); + } + + RealSocket::log.logMessage(logger::LogLevel::INFO, "connection succeed to client socket number: " + std::to_string(sockfd)); + return newSocket; +} + +int RealSocket::connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + int connectAns = ::connect(sockfd, addr, addrlen); + if (connectAns < 0) + { + RealSocket::log.logMessage(logger::LogLevel::ERROR, "process", "server", "Connection Failed: " + std::string(strerror(errno))); + } + + RealSocket::log.logMessage(logger::LogLevel::INFO, "process", "server", "connection succeed: " + std::string(strerror(errno))); + return connectAns; +} + +ssize_t RealSocket::recv(int sockfd, void *buf, size_t len, int flags) +{ + int valread = ::recv(sockfd, buf, len, flags); + const Packet *p = static_cast(buf); + + if (valread < 0) + RealSocket::log.logMessage(logger::LogLevel::ERROR, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string(" Error occurred: in socket ") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); + else if (valread == 0) + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string(" connection closed: in socket ") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); + else { + if (!p->header.DLC) + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string("received packet number: ") + std::to_string(p->header.PSN) + std::string(", of messageId: ") + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno)) + " ID for connection: " + std::to_string(p->header.SrcID)); + else + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string("received packet number: ") + std::to_string(p->header.PSN) + std::string(", of messageId: ") + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno)) + " Data: " + p->pointerToHex(p->data,p->header.DLC)); + } + + + return valread; +} + +ssize_t RealSocket::send(int sockfd, const void *buf, size_t len, int flags) +{ + int sendAns = ::send(sockfd, buf, len, flags); + const Packet *p = static_cast(buf); + if (sendAns <= 0) + { + RealSocket::log.logMessage(logger::LogLevel::ERROR, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), "sending packet number: " + std::to_string(p->header.PSN) + ", of messageId: " + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno))); + } + if (!p->header.DLC) + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), "sending packet number: " + std::to_string(p->header.PSN) + ", of messageId: " + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno)) + " ID for connection: " + std::to_string(p->header.SrcID)); + else + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), "sending packet number: " + std::to_string(p->header.PSN) + ", of messageId: " + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno)) + " Data: " + p->pointerToHex(p->data,p->header.DLC)); + return sendAns; +} + +int RealSocket::close(int fd) +{ + RealSocket::log.logMessage(logger::LogLevel::INFO, "close socket number: " + std::to_string(fd)); + RealSocket::log.cleanUp(); + shutdown(fd, SHUT_RDWR); + return ::close(fd); +} \ No newline at end of file diff --git a/communication/sockets/real_socket.h b/communication/sockets/real_socket.h index a9be3823..f9eecb51 100644 --- a/communication/sockets/real_socket.h +++ b/communication/sockets/real_socket.h @@ -4,121 +4,31 @@ #include "Isocket.h" #include #include -#include "../../logger/logger.h" #include "../include/packet.h" class RealSocket : public ISocket { -private: - logger log; - public: + static logger log; - RealSocket() - { - log = logger("communication"); - } - - int socket(int domain, int type, int protocol) override - { - int sockFd = ::socket(domain, type, protocol); - if (sockFd < 0) { - log.logMessage(logger::LogLevel::ERROR, "socket creation error: " + std::string(strerror(errno))); - } - log.logMessage(logger::LogLevel::INFO, "create a client socket: " + std::to_string(sockFd) + std::string(" ") + std::string(strerror(errno))); - return sockFd; - } - - int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) override - { - int sockopt = ::setsockopt(sockfd, level, optname, optval, optlen); - if (sockopt) { - log.logMessage(logger::LogLevel::ERROR, "setsockopt failed: " + std::string(strerror(errno))); - close(sockfd); - } - - log.logMessage(logger::LogLevel::INFO, "create a server socket: " + std::to_string(sockfd)); - return sockopt; - } - - int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) override - { - int bindAns = ::bind(sockfd, addr, addrlen); - if (bindAns < 0) { - log.logMessage(logger::LogLevel::ERROR, "Bind failed: " + std::string(strerror(errno))); - close(sockfd); - } - - return bindAns; - } + RealSocket(); - int listen(int sockfd, int backlog) override - { - int listenAns = ::listen(sockfd, backlog); - if (listenAns < 0) { - log.logMessage(logger::LogLevel::ERROR, "Listen failed: " + std::string(strerror(errno))); - close(sockfd); - } + int socket(int domain, int type, int protocol) override; - log.logMessage(logger::LogLevel::INFO, "server running on port " + std::to_string(8080)); - return listenAns; - } + int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) override; - int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) override - { - int newSocket = ::accept(sockfd, addr, addrlen); - if (newSocket < 0) { - log.logMessage(logger::LogLevel::ERROR, "Accept failed: " + std::string(strerror(errno))); - } + int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) override; - log.logMessage(logger::LogLevel::INFO, "connection succeed to client socket number: " + std::to_string(sockfd)); - return newSocket; - } - - int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) override - { - int connectAns = ::connect(sockfd, addr, addrlen); - if (connectAns < 0) { - log.logMessage(logger::LogLevel::ERROR, "process", "server", "Connection Failed: " + std::string(strerror(errno))); - } - - log.logMessage(logger::LogLevel::INFO, "process", "server", "connection succeed: " + std::string(strerror(errno))); - return connectAns; - } - - ssize_t recv(int sockfd, void *buf, size_t len, int flags) override - { - int valread = ::recv(sockfd, buf, len, flags); - const Packet *p = static_cast(buf); - - if (valread < 0) - log.logMessage(logger::LogLevel::ERROR, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string("Error occurred: in socket ") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); - else if( valread == 0) - log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string(" connection closed: in socket") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); - else - log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string("received packet number: ") + std::to_string(p->header.PSN) + std::string(", of messageId: ") + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno))); - return valread; - } + int listen(int sockfd, int backlog) override; + + int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) override; + + int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) override; - ssize_t send(int sockfd, const void *buf, size_t len, int flags) override - { - int sendAns = ::send(sockfd, buf, len, flags); - const Packet *p = static_cast(buf); - if(sendAns < 0) { - log.logMessage(logger::LogLevel::ERROR, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), "sending packet number: " + std::to_string(p->header.PSN) + ", of messageId: " + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno))); - } + ssize_t recv(int sockfd, void *buf, size_t len, int flags) override; - log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), "sending packet number: " + std::to_string(p->header.PSN) + ", of messageId: " + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno))); - return sendAns; - } + ssize_t send(int sockfd, const void *buf, size_t len, int flags) override; - int close(int fd) override - { - log.logMessage(logger::LogLevel::INFO, "close socket number: " + std::to_string(fd)); - log.cleanUp(); - shutdown(fd, SHUT_RDWR); - return ::close(fd); - } + int close(int fd) override; }; - #endif \ No newline at end of file diff --git a/communication/src/bus_manager.cpp b/communication/src/bus_manager.cpp index d84098ba..225be049 100644 --- a/communication/src/bus_manager.cpp +++ b/communication/src/bus_manager.cpp @@ -4,7 +4,7 @@ BusManager* BusManager::instance = nullptr; std::mutex BusManager::managerMutex; //Private constructor -BusManager::BusManager(std::vector idShouldConnect, uint32_t limit) : server(8080, std::bind(&BusManager::receiveData, this, std::placeholders::_1)) //,syncCommunication(idShouldConnect, limit) +BusManager::BusManager(std::vector idShouldConnect, uint32_t limit) :server(8080, std::bind(&BusManager::receiveData, this, std::placeholders::_1))//,syncCommunication(idShouldConnect, limit) { // Setup the signal handler for SIGINT signal(SIGINT, BusManager::signalHandler); @@ -23,19 +23,19 @@ BusManager* BusManager::getInstance(std::vector idShouldConnect, uint3 } // Sends to the server to listen for requests -int BusManager::startConnection() +ErrorCode BusManager::startConnection() { - int isConnected = server.startConnection(); + ErrorCode isConnected = server.startConnection(); //syncCommunication.notifyProcess() return isConnected; } // Receives the packet that arrived and checks it before sending it out -void BusManager::receiveData(void *data) +void BusManager::receiveData(Packet &p) { - Packet *p = static_cast(data); - int res = sendToClients(*p); + ErrorCode res = sendToClients(p); + // Checking the case of collision and priority in functions : checkCollision,packetPriority // Packet* resolvedPacket = checkCollision(*p); // if (resolvedPacket) @@ -43,7 +43,7 @@ void BusManager::receiveData(void *data) } // Sending according to broadcast variable -int BusManager::sendToClients(const Packet &packet) +ErrorCode BusManager::sendToClients(const Packet &packet) { if(packet.header.isBroadcast) return server.sendBroadcast(packet); diff --git a/communication/src/client.cpp b/communication/src/client.cpp deleted file mode 100644 index 881dd635..00000000 --- a/communication/src/client.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "../include/client.h" - -// Constructor -Client::Client(uint32_t id, std::function callback, ISocket* socketInterface) - : id(id), passPacketCom(callback), connected(false), socketInterface(socketInterface) {} - -// Requesting a connection to the server -int Client::connectToServer() -{ - clientSocket = socketInterface->socket(AF_INET, SOCK_STREAM, 0); - if (clientSocket < 0) { - return -1; - } - - servAddress.sin_family = AF_INET; - servAddress.sin_port = htons(PORT); - inet_pton(AF_INET, IP, &servAddress.sin_addr); - - int connectRes = socketInterface->connect(clientSocket, (struct sockaddr *)&servAddress, sizeof(servAddress)); - if (connectRes < 0) { - return -1; - } - - Packet packet(id); - ssize_t bytesSent = socketInterface->send(clientSocket, &packet, sizeof(Packet), 0); - if (bytesSent < sizeof(Packet)) { - return -1; - } - - connected = true; - receiveThread = std::thread(&Client::receivePacket, this); - receiveThread.detach(); - - return 0; -} - -// Sends the packet to the manager-sync -int Client::sendPacket(Packet &packet) -{ - //If send executed before start - if (!connected) - return -1; - - ssize_t bytesSent = socketInterface->send(clientSocket, &packet, sizeof(Packet), 0); - if (bytesSent < sizeof(Packet)) { - return -1; - } - - return 0; -} - -// Waits for a message and forwards it to Communication -void Client::receivePacket() -{ - Packet packet; - while (connected) { - int valread = socketInterface->recv(clientSocket, &packet, sizeof(Packet), 0); - if (valread <= 0) { - break; - } - passPacketCom(packet); - } -} - -// Closes the connection -void Client::closeConnection() -{ - //implement - Notify the manager about disconnection - connected = false; - socketInterface->close(clientSocket); - if(receiveThread.joinable()) - receiveThread.join(); -} - -//For testing -int Client::getClientSocket() -{ - return clientSocket; -} - -int Client::isConnected() -{ - return connected; -} - -bool Client::isReceiveThreadRunning() -{ - return false; -} - -//Destructor -Client::~Client() -{ - closeConnection(); - delete socketInterface; -} \ No newline at end of file diff --git a/communication/src/client_connection.cpp b/communication/src/client_connection.cpp new file mode 100644 index 00000000..d3035c72 --- /dev/null +++ b/communication/src/client_connection.cpp @@ -0,0 +1,127 @@ +#include "../include/client_connection.h" + +// Constructor +ClientConnection::ClientConnection(std::function callback, ISocket* socketInterface): connected(false){ + setCallback(callback); + setSocketInterface(socketInterface); +} + +// Requesting a connection to the server +ErrorCode ClientConnection::connectToServer(int id) +{ + clientSocket = socketInterface->socket(AF_INET, SOCK_STREAM, 0); + if (clientSocket < 0) { + return ErrorCode::SOCKET_FAILED; + } + + servAddress.sin_family = AF_INET; + servAddress.sin_port = htons(PORT); + inet_pton(AF_INET, IP, &servAddress.sin_addr); + + int connectRes = socketInterface->connect(clientSocket, (struct sockaddr *)&servAddress, sizeof(servAddress)); + if (connectRes < 0) { + socketInterface->close(clientSocket); + return ErrorCode::CONNECTION_FAILED; + } + + Packet packet(id); + ssize_t bytesSent = socketInterface->send(clientSocket, &packet, sizeof(Packet), 0); + if (bytesSent < sizeof(Packet)) { + socketInterface->close(clientSocket); + return ErrorCode::SEND_FAILED; + } + + connected = true; + receiveThread = std::thread(&ClientConnection::receivePacket, this); + receiveThread.detach(); + + return ErrorCode::SUCCESS; +} + +// Sends the packet to the manager-sync +ErrorCode ClientConnection::sendPacket(Packet &packet) +{ + //If send executed before start + if (!connected) + return ErrorCode::CONNECTION_FAILED; + + ssize_t bytesSent = socketInterface->send(clientSocket, &packet, sizeof(Packet), 0); + if (bytesSent==0) { + closeConnection(); + return ErrorCode::CONNECTION_FAILED; + } + + if (bytesSent<0) + return ErrorCode::SEND_FAILED; + + return ErrorCode::SUCCESS; +} + +// Waits for a message and forwards it to Communication +void ClientConnection::receivePacket() +{ + while (connected) { + Packet packet; + int valread = socketInterface->recv(clientSocket, &packet, sizeof(Packet), 0); + if (valread==0) + break; + + if (valread<0) + continue; + + passPacketCom(packet); + } + + closeConnection(); +} + +// Closes the connection +ErrorCode ClientConnection::closeConnection() +{ + if (connected) { + int socketInterfaceRes = socketInterface->close(clientSocket); + if(socketInterfaceRes < 0) + return ErrorCode::CLOSE_FAILED; + connected = false; + } + return ErrorCode::SUCCESS; +} + +// Setter for passPacketCom +void ClientConnection::setCallback(std::function callback) { + if (!callback) + throw std::invalid_argument("Callback function cannot be null"); + + passPacketCom = callback; +} + +// Setter for socketInterface +void ClientConnection::setSocketInterface(ISocket* socketInterface) { + if (!socketInterface) + throw std::invalid_argument("Socket interface cannot be null"); + + this->socketInterface = socketInterface; +} + +// For testing +int ClientConnection::getClientSocket() +{ + return clientSocket; +} + +int ClientConnection::isConnected() +{ + return connected; +} + +bool ClientConnection::isReceiveThreadRunning() +{ + return false; +} + +//Destructor +ClientConnection::~ClientConnection() +{ + closeConnection(); + delete socketInterface; +} \ No newline at end of file diff --git a/communication/src/communication.cpp b/communication/src/communication.cpp index 2cf7f2bb..3ec8ff70 100644 --- a/communication/src/communication.cpp +++ b/communication/src/communication.cpp @@ -4,51 +4,68 @@ Communication* Communication::instance = nullptr; // Constructor -Communication::Communication(uint32_t id, void (*passDataCallback)(void *)) : client(id, std::bind(&Communication::receivePacket, this, std::placeholders::_1)), passData(passDataCallback), id(id) +Communication::Communication(uint32_t id, void (*passDataCallback)(uint32_t, void *)) : + client(std::bind(&Communication::receivePacket, this, std::placeholders::_1)) { + setId(id); + setPassDataCallback(passDataCallback); + instance = this; - // Setup the signal handler for SIGINT - signal(SIGINT, Communication::signalHandler); + + auto signalResult = signal(SIGINT, Communication::signalHandler); + if (signalResult == SIG_ERR) + throw std::runtime_error("Failed to set signal handler for SIGINT"); } // Sends the client to connect to server -void Communication::startConnection() +ErrorCode Communication::startConnection() { //Waiting for manager //syncCommunication.isManagerRunning() - int isConnected = client.connectToServer(); + ErrorCode isConnected = client.connectToServer(id); //Increases the shared memory and blocks the process - if not all are connected //syncCommunication.registerProcess() return isConnected; } // Sends a message sync -int Communication::sendMessage(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, bool isBroadcast) +ErrorCode Communication::sendMessage(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, bool isBroadcast) { - // Creating a message and dividing it into packets + if (dataSize == 0) + return ErrorCode::INVALID_DATA_SIZE; + + if (data == nullptr) + return ErrorCode::INVALID_DATA; + + if (!client.isConnected()) + return ErrorCode::CONNECTION_FAILED; + Message msg(srcID, data, dataSize, isBroadcast, destID); + //Sending the message to logger + RealSocket::log.logMessage(logger::LogLevel::INFO,std::to_string(srcID),std::to_string(destID),"Complete message:" + msg.getPackets().at(0).pointerToHex(data, dataSize)); + for (auto &packet : msg.getPackets()) { - int res = client.sendPacket(packet); - if(res < 0) + ErrorCode res = client.sendPacket(packet); + if (res != ErrorCode::SUCCESS) return res; } - return 0; + return ErrorCode::SUCCESS; } // Sends a message Async -void Communication::sendMessageAsync(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, std::function sendCallback, bool isBroadcast) +void Communication::sendMessageAsync(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, std::function sendCallback, bool isBroadcast) { - std::promise resultPromise; - std::future resultFuture = resultPromise.get_future(); + std::promise resultPromise; + std::future resultFuture = resultPromise.get_future(); std::thread([this, data, dataSize, destID, srcID, isBroadcast, &resultPromise]() { - int res = this->sendMessage(data, dataSize, destID, srcID, isBroadcast); + ErrorCode res = this->sendMessage(data, dataSize, destID, srcID, isBroadcast); resultPromise.set_value(res); }).detach(); - int res = resultFuture.get(); + ErrorCode res = resultFuture.get(); sendCallback(res); } @@ -100,9 +117,7 @@ Packet Communication::hadArrived() // Adding the packet to the complete message void Communication::addPacketToMessage(Packet &p) { - // messageId may have to change according to the CAN bus - std::string messageId = std::to_string(p.header.SrcID) + "-" + std::to_string(p.header.DestID); - + std::string messageId = std::to_string(p.header.ID); // If the message already exists, we will add the packet if (receivedMessages.find(messageId) != receivedMessages.end()) { receivedMessages[messageId].addPacket(p); @@ -116,7 +131,7 @@ void Communication::addPacketToMessage(Packet &p) // If the message is complete, we pass the data to the passData function if (receivedMessages[messageId].isComplete()) { void *completeData = receivedMessages[messageId].completeData(); - passData(completeData); + passData(p.header.SrcID, completeData); receivedMessages.erase(messageId); // Removing the message once completed } } @@ -124,13 +139,25 @@ void Communication::addPacketToMessage(Packet &p) // Static method to handle SIGINT signal void Communication::signalHandler(int signum) { - std::cout << "Interrupt signal (" << signum << ") received.\n"; - if (instance) { - instance->client.closeConnection(); // Call the closeConnection method - } + if (instance) + instance->client.closeConnection(); + exit(signum); } +void Communication::setId(uint32_t newId) +{ + id = newId; +} + +void Communication::setPassDataCallback(void (*callback)(uint32_t, void *)) +{ + if (callback == nullptr) + throw std::invalid_argument("Invalid callback function: passDataCallback cannot be null"); + + passData = callback; +} + //Destructor Communication::~Communication() { instance = nullptr; diff --git a/communication/src/error_code.h b/communication/src/error_code.h new file mode 100644 index 00000000..961767c0 --- /dev/null +++ b/communication/src/error_code.h @@ -0,0 +1,46 @@ +#ifndef ERRORCODE_H +#define ERRORCODE_H + +enum class ErrorCode { + SUCCESS = 0, + SOCKET_FAILED = -1, + CONNECTION_FAILED = -2, + BIND_FAILED = -3, + LISTEN_FAILED = -4, + ACCEPT_FAILED = -5, + SEND_FAILED = -6, + RECEIVE_FAILED = -7, + CLOSE_FAILED = -8, + DISCONNECT_FAILED = -9, + INVALID_CLIENT_ID = -10, + CALLBACK_ERROR = -11, + SOCKET_INTERFACE_ERROR = -12, + INVALID_DATA_SIZE = -13, + INVALID_DATA = -14, + INVALID_ID = -15 +}; + +// Function to convert ErrorCode to string +inline const char* toString(ErrorCode error) { + switch (error) { + case ErrorCode::SUCCESS: return "SUCCESS"; + case ErrorCode::SOCKET_FAILED: return "SOCKET_FAILED"; + case ErrorCode::CONNECTION_FAILED: return "CONNECTION_FAILED"; + case ErrorCode::SEND_FAILED: return "SEND_FAILED"; + case ErrorCode::RECEIVE_FAILED: return "RECEIVE_FAILED"; + case ErrorCode::CLOSE_FAILED: return "CLOSE_FAILED"; + case ErrorCode::CALLBACK_ERROR: return "CALLBACK_ERROR"; + case ErrorCode::SOCKET_INTERFACE_ERROR: return "SOCKET_INTERFACE_ERROR"; + case ErrorCode::BIND_FAILED: return "BIND_FAILED"; + case ErrorCode::LISTEN_FAILED: return "LISTEN_FAILED"; + case ErrorCode::ACCEPT_FAILED: return "ACCEPT_FAILED"; + case ErrorCode::INVALID_CLIENT_ID: return "INVALID_CLIENT_ID"; + case ErrorCode::DISCONNECT_FAILED: return "DISCONNECT_FAILED"; + case ErrorCode::INVALID_DATA_SIZE: return "INVALID_DATA_SIZE"; + case ErrorCode::INVALID_DATA: return "INVALID_DATA"; + case ErrorCode::INVALID_ID: return "INVALID_ID"; + default: return "UNKNOWN_ERROR"; + } +} + +#endif // ERRORCODE_H diff --git a/communication/src/message.cpp b/communication/src/message.cpp index d6cd1174..35da7a15 100644 --- a/communication/src/message.cpp +++ b/communication/src/message.cpp @@ -4,11 +4,11 @@ Message::Message(uint32_t srcID, void *data, int dlc, bool isBroadcast, uint32_t destID) { size_t size = dlc; - uint32_t tps = (size + 7) / 8; // Calculate the number of packets needed + uint32_t tps = (size + SIZE_PACKET-1) / SIZE_PACKET; // Calculate the number of packets needed for (uint32_t i = 0; i < tps; ++i) { - uint8_t packetData[8]; - size_t copySize = std::min(size - i * 8, (size_t)8); // Determine how much data to copy for each packet - std::memcpy(packetData, (uint8_t *)data + i * 8, copySize); + uint8_t packetData[SIZE_PACKET]; + size_t copySize = std::min(size - i * SIZE_PACKET, (size_t)SIZE_PACKET); // Determine how much data to copy for each packet + std::memcpy(packetData, (uint8_t *)data + i * SIZE_PACKET, copySize); uint32_t id = srcID + destID; packets.emplace_back(id, i, tps, srcID, destID, packetData, copySize, isBroadcast, false, false); } @@ -43,10 +43,10 @@ bool Message::isComplete() const // Get the complete data of the message void *Message::completeData() const { - size_t totalSize = (tps - 1) * 8 + packets.back().header.DLC; + size_t totalSize = (tps - 1) * SIZE_PACKET + packets.back().header.DLC; void *data = malloc(totalSize); for (const auto &packet : packets) { - std::memcpy((uint8_t *)data + packet.header.PSN * 8, packet.data, packet.header.DLC); + std::memcpy(static_cast(data) + packet.header.PSN * SIZE_PACKET, packet.data, packet.header.DLC); } return data; } diff --git a/communication/src/packet.cpp b/communication/src/packet.cpp index 907a5962..507755ed 100644 --- a/communication/src/packet.cpp +++ b/communication/src/packet.cpp @@ -1,6 +1,6 @@ #include "../include/packet.h" // Constructor to initialize Packet for sending -Packet::Packet(uint32_t id, uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, uint8_t *data, int dlc, bool isBroadcast, bool RTR, bool passive) +Packet::Packet(uint32_t id, uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, void *data, uint8_t dlc, bool isBroadcast, bool RTR, bool passive) { header.ID = id; header.PSN = psn; @@ -25,9 +25,21 @@ Packet::Packet(uint32_t id) } // Implementation according to the CAN BUS -uint16_t Packet::calculateCRC(const uint8_t *data, size_t length) +uint16_t Packet::calculateCRC(const void *data, size_t length) { uint16_t crc = 0xFFFF; // Calculate CRC for the given data and length return crc; +} + +// A function to convert the data to hexa (logger) +std::string Packet::pointerToHex(const void* data, size_t size) const +{ + const unsigned char* byteData = reinterpret_cast(data); + std::ostringstream oss; + oss<<"0x"; + for (size_t i = 0; i < size; ++i) + oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(byteData[i]); + + return oss.str(); } \ No newline at end of file diff --git a/communication/src/server.cpp b/communication/src/server.cpp deleted file mode 100644 index 816fe221..00000000 --- a/communication/src/server.cpp +++ /dev/null @@ -1,178 +0,0 @@ -#include "../include/server.h" - -// Constructor -Server::Server(int port, std::function callback, ISocket* socketInterface) - : port(port), receiveDataCallback(callback), running(false), socketInterface(socketInterface) {} - -// Initializes the listening socket -int Server::startConnection() -{ - // Create socket TCP - serverSocket = socketInterface->socket(AF_INET, SOCK_STREAM, 0); - if (serverSocket < 0) { - return -1; - } - - // Setting the socket to allow reuse of address and port - int opt = 1; - int setSockOptRes = socketInterface->setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); - if (setSockOptRes) { - socketInterface->close(serverSocket); - return -1; - } - - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(port); - - int bindRes = socketInterface->bind(serverSocket, (struct sockaddr *)&address, sizeof(address)); - if (bindRes < 0) { - socketInterface->close(serverSocket); - } - - int lisRes = socketInterface->listen(serverSocket, 5); - if (lisRes < 0) { - socketInterface->close(serverSocket); - return -1; - } - - running = true; - mainThread = std::thread(&Server::startThread, this); - mainThread.detach(); - - return 0; -} - -// Starts listening for connection requests -void Server::startThread() -{ - while (running) { - int clientSocket = socketInterface->accept(serverSocket, nullptr, nullptr); - if (clientSocket < 0) { - return; - } - // Opens a new thread for handleClient - listening to messages from the process - { - std::lock_guard lock(threadMutex); - clientThreads.emplace_back(&Server::handleClient, this, clientSocket); - } - } -} - -// Implementation according to the CAN BUS -bool Server::isValidId(uint32_t id) -{ - return true; -} - -// Closes the sockets and the threads -void Server::stopServer() -{ - running = false; - socketInterface->close(serverSocket); - { - std::lock_guard lock(socketMutex); - for (int sock : sockets) - socketInterface->close(sock); - } - { - std::lock_guard lock(threadMutex); - for (auto &th : clientThreads) - if (th.joinable()) - th.join(); - } - if(mainThread.joinable()) - mainThread.join(); -} - -// Runs in a thread for each process - waits for a message and forwards it to the manager -void Server::handleClient(int clientSocket) -{ - Packet packet; - int valread = socketInterface->recv(clientSocket, &packet, sizeof(Packet), 0); - if (valread <= 0) - return; - - uint32_t clientID = packet.header.SrcID; - if(!isValidId(clientID)) - return; - { - std::lock_guard lock(IDMapMutex); - clientIDMap[clientSocket] = clientID; - } - - { - std::lock_guard lock(socketMutex); - sockets.push_back(clientSocket); - } - - while (true) { - int valread = socketInterface->recv(clientSocket, &packet, sizeof(Packet), 0); - if (valread <= 0) - break; - - receiveDataCallback(&packet); - } - - // If the process is no longer connected - std::lock_guard lock(socketMutex); - auto it = std::find(sockets.begin(), sockets.end(), clientSocket); - if (it != sockets.end()) - sockets.erase(it); -} - -// Returns the sockets ID -int Server::getClientSocketByID(uint32_t destID) -{ - std::lock_guard lock(IDMapMutex); - for (const auto &client : clientIDMap) - if (client.second == destID) - return client.first; - - return -1; -} - -// Sends the message to destination -int Server::sendDestination(const Packet &packet) -{ - int targetSocket = getClientSocketByID(packet.header.DestID); - if (targetSocket == -1) - return -1; - - ssize_t bytesSent = socketInterface->send(targetSocket, &packet, sizeof(Packet), 0); - if (bytesSent < sizeof(Packet)) - return -1; - - return 0; -} - -// Sends the message to all connected processes - broadcast -int Server::sendBroadcast(const Packet &packet) -{ - std::lock_guard lock(socketMutex); - for (int sock : sockets) { - ssize_t bytesSent = socketInterface->send(sock, &packet, sizeof(Packet), 0); - if (bytesSent < sizeof(Packet)) - return -1; - } - - return 0; -} - -// For testing -int Server::getServerSocket() -{ - return serverSocket; -} - -int Server::isRunning() -{ - return running; -} - -// Destructor -Server::~Server() -{ - stopServer(); - delete socketInterface; -} \ No newline at end of file diff --git a/communication/src/server_connection.cpp b/communication/src/server_connection.cpp new file mode 100644 index 00000000..c8453e73 --- /dev/null +++ b/communication/src/server_connection.cpp @@ -0,0 +1,267 @@ +#include +#include +#include "../include/server_connection.h" + +// Constructor +ServerConnection::ServerConnection(int port, std::function callback, ISocket* socketInterface) { + setPort(port); + setReceiveDataCallback(callback); + setSocketInterface(socketInterface); + running = false; +} + +// Initializes the listening socket +ErrorCode ServerConnection::startConnection() +{ + // Create socket TCP + serverSocket = socketInterface->socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket < 0) + return ErrorCode::SOCKET_FAILED; + + // Setting the socket to allow reuse of address and port + int opt = 1; + int setSockOptRes = socketInterface->setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); + if (setSockOptRes) { + socketInterface->close(serverSocket); + return ErrorCode::SOCKET_FAILED; + } + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + + int bindRes = socketInterface->bind(serverSocket, (struct sockaddr *)&address, sizeof(address)); + if (bindRes < 0) { + socketInterface->close(serverSocket); + return ErrorCode::BIND_FAILED; + } + + int lisRes = socketInterface->listen(serverSocket, 5); + if (lisRes < 0) { + socketInterface->close(serverSocket); + return ErrorCode::LISTEN_FAILED; + } + + running = true; + mainThread = std::thread(&ServerConnection::startThread, this); + mainThread.detach(); + + return ErrorCode::SUCCESS; +} + +// Starts listening for connection requests +void ServerConnection::startThread() +{ + while (running) { + int clientSocket = socketInterface->accept(serverSocket, nullptr, nullptr); + if (!clientSocket) + continue; + + if(clientSocket<0){ + stopServer(); + return; + } + // Opens a new thread for handleClient - listening to messages from the process + { + std::lock_guard lock(threadMutex); + clientThreads.emplace_back(&ServerConnection::handleClient, this, clientSocket); + } + } +} + +// Closes the sockets and the threads +void ServerConnection::stopServer() +{ + if(!running) + return; + + running = false; + socketInterface->close(serverSocket); + { + std::lock_guard lock(socketMutex); + for (int sock : sockets) + socketInterface->close(sock); + sockets.clear(); + } + { + std::lock_guard lock(threadMutex); + for (auto &th : clientThreads) + if (th.joinable()) + th.join(); + } +} + +// Runs in a thread for each process - waits for a message and forwards it to the manager +void ServerConnection::handleClient(int clientSocket) +{ + Packet packet; + int valread = socketInterface->recv(clientSocket, &packet, sizeof(Packet), 0); + + //implement according to CAN bus + if (valread <= 0) + return; + + uint32_t clientID = packet.header.SrcID; + if(!isValidId(clientID)) + return; + + { + std::lock_guard lock(IDMapMutex); + clientIDMap[clientSocket] = clientID; + } + + { + std::lock_guard lock(socketMutex); + sockets.push_back(clientSocket); + } + + while (running) { + int valread = socketInterface->recv(clientSocket, &packet, sizeof(Packet), 0); + if (valread == 0) + break; + + if(valread < 0) + continue; + + receiveDataCallback(packet); + } + + { + // If the process is no longer connected + std::lock_guard lock(socketMutex); + auto it = std::find(sockets.begin(), sockets.end(), clientSocket); + socketInterface->close(*it); + if (it != sockets.end()) + sockets.erase(it); + } + { + std::lock_guard lock(IDMapMutex); + clientIDMap.erase(clientSocket); + } +} + +// Implementation according to the CAN BUS +bool ServerConnection::isValidId(uint32_t id) +{ + std::lock_guard lock(IDMapMutex); + return clientIDMap.find(id) == clientIDMap.end(); +} + +// Returns the sockets ID +int ServerConnection::getClientSocketByID(uint32_t destID) +{ + std::lock_guard lock(IDMapMutex); + for (const auto &client : clientIDMap) + if (client.second == destID) + return client.first; + + return -1; +} + +// Sends the message to destination +ErrorCode ServerConnection::sendDestination(const Packet &packet) +{ + int targetSocket = getClientSocketByID(packet.header.DestID); + if (targetSocket == -1) + return ErrorCode::INVALID_CLIENT_ID; + + ssize_t bytesSent = socketInterface->send(targetSocket, &packet, sizeof(Packet), 0); + if (!bytesSent) + return ErrorCode::SEND_FAILED; + + if (bytesSent<0){ + //closeConnection(); + return ErrorCode::CONNECTION_FAILED; + } + + return ErrorCode::SUCCESS; +} + +// Sends the message to all connected processes - broadcast +ErrorCode ServerConnection::sendBroadcast(const Packet &packet) +{ + std::lock_guard lock(socketMutex); + for (int sock : sockets) { + ssize_t bytesSent = socketInterface->send(sock, &packet, sizeof(Packet), 0); + if (bytesSent < sizeof(Packet)) + return ErrorCode::SEND_FAILED; + if (bytesSent<0){ + //closeConnection(); + return ErrorCode::CONNECTION_FAILED; + } + } + + return ErrorCode::SUCCESS; +} + +// Sets the server's port number, throws an exception if the port is invalid. +void ServerConnection::setPort(int port) { + if (port <= 0 || port > 65535) + throw std::invalid_argument("Invalid port number: Port must be between 1 and 65535."); + + this->port = port; +} + +// Sets the callback for receiving data, throws an exception if the callback is null. +void ServerConnection::setReceiveDataCallback(std::function callback) { + if (!callback) { + throw std::invalid_argument("Invalid callback function: callback cannot be null."); + } + this->receiveDataCallback = callback; +} + +// Sets the socket interface, throws an exception if the socketInterface is null. +void ServerConnection::setSocketInterface(ISocket* socketInterface) { + if (socketInterface == nullptr) { + throw std::invalid_argument("Invalid socket interface: socketInterface cannot be null."); + } + this->socketInterface = socketInterface; +} + +// For testing +int ServerConnection::getServerSocket() +{ + return serverSocket; +} + +int ServerConnection::isRunning() +{ + return running; +} + +std::vector* ServerConnection::getSockets() +{ + return &sockets; +} + +std::mutex* ServerConnection::getSocketMutex() +{ + return &socketMutex; +} + +std::mutex* ServerConnection::getIDMapMutex() +{ + return &IDMapMutex; +} + +std::map* ServerConnection::getClientIDMap() +{ + return &clientIDMap; +} + +void ServerConnection::testHandleClient(int clientSocket) +{ + handleClient(clientSocket); +} + +int ServerConnection::testGetClientSocketByID(uint32_t destID) +{ + return getClientSocketByID(destID); +} + +// Destructor +ServerConnection::~ServerConnection() +{ + stopServer(); + delete socketInterface; +} \ No newline at end of file diff --git a/tests/client_test.cpp b/tests/client_test.cpp index 44f5e4c7..ba57e1c5 100644 --- a/tests/client_test.cpp +++ b/tests/client_test.cpp @@ -1,9 +1,8 @@ - #include #include -#include "../src/client.h" -#include "../sockets/mock_socket.h" -#include "../src/message.h" +#include "../communication/src/client.h" +#include "../communication/sockets/mock_socket.h" +#include "../communication/src/message.h" using ::testing::_; using ::testing::Return; @@ -11,10 +10,9 @@ using ::testing::Return; class ClientTest : public ::testing::Test { protected: MockSocket mockSocket; - Client *client; + Client* client; void SetUp() override { - ON_CALL(mockSocket, socket(::testing::_, ::testing::_, ::testing::_)) .WillByDefault(::testing::Return(1)); ON_CALL(mockSocket, setsockopt(::testing::_, ::testing::_, ::testing::_, ::testing::NotNull(), ::testing::_)) @@ -22,52 +20,184 @@ class ClientTest : public ::testing::Test { ON_CALL(mockSocket, bind(::testing::_, ::testing::_, ::testing::_)) .WillByDefault(::testing::Return(0)); ON_CALL(mockSocket, listen(::testing::_, ::testing::_)) - .WillByDefault(::testing::Return(0)); + .WillByDefault(::testing::Return(0)); ON_CALL(mockSocket, connect(_, _, _)) .WillByDefault(::testing::Return(0)); ON_CALL(mockSocket, close(_)) .WillByDefault(::testing::Return(0)); + + ON_CALL(mockSocket, socket(_, _, _)) + .WillByDefault(Return(1)); + + ON_CALL(mockSocket, setsockopt(_, _, _, _, _)) + .WillByDefault(Return(0)); + + ON_CALL(mockSocket, connect(_, _, _)) + .WillByDefault(Return(0)); + + ON_CALL(mockSocket, send(_, _, _, _)) + .WillByDefault(Return(44)); // ודא ששליחה מחזירה הצלחה + + ON_CALL(mockSocket, close(_)) + .WillByDefault(Return(0)); // close מחזיר הצלחה + + EXPECT_CALL(mockSocket, close(_)).Times(1); // מצפה ש-close תוקרא פעם אחת + + Packet packet; ON_CALL(mockSocket, send(_, _, _, _)) .WillByDefault(::testing::Return(sizeof(Packet))); - - client= new Client(1, [](Packet&){}, &mockSocket); + + client = new Client([](Packet &){}, &mockSocket); } + void TearDown() override { - // delete client; + EXPECT_CALL(mockSocket, close(_)).Times(1); + //delete client; } }; -TEST_F(ClientTest, ConstructorCreatesSocket) { - EXPECT_GT(client->getClientSocket(), 0); +// Test Constructor +TEST_F(ClientTest, ConstructorInitializesCorrectly) { + EXPECT_FALSE(client->isConnected()); + EXPECT_GT(client->getClientSocket(), 0); } +// Test connection success TEST_F(ClientTest, ConnectToServerSuccess) { - int result = client->connectToServer(); - EXPECT_EQ(result, 0); + EXPECT_CALL(mockSocket, connect(_, _, _)).WillOnce(Return(0)); + ErrorCode result = client->connectToServer(1); + EXPECT_EQ(result, ErrorCode::SUCCESS); + EXPECT_TRUE(client->isConnected()); } -TEST_F(ClientTest, ConnectToServerFailure) { - EXPECT_CALL(mockSocket, connect(_, _, _)) - .WillOnce(Return(-1)); - int result = client->connectToServer(); - EXPECT_EQ(result, -1); +// Test connection failure (connect fails) +TEST_F(ClientTest, ConnectToServerFailureOnConnect) { + EXPECT_CALL(mockSocket, connect(_, _, _)).WillOnce(Return(-1)); + ErrorCode result = client->connectToServer(1); + EXPECT_EQ(result, ErrorCode::CONNECTION_FAILED); + EXPECT_FALSE(client->isConnected()); +} + +// Test connection failure (socket creation fails) +TEST_F(ClientTest, ConnectToServerSocketFailure) { + EXPECT_CALL(mockSocket, socket(_, _, _)).WillOnce(Return(-1)); + ErrorCode result = client->connectToServer(1); + EXPECT_EQ(result, ErrorCode::SOCKET_FAILED); + EXPECT_FALSE(client->isConnected()); } +// Test sendPacket success TEST_F(ClientTest, SendPacketSuccess) { Packet packet; - client->connectToServer(); - int result = client->sendPacket(packet); - EXPECT_EQ(result, 0); + client->connectToServer(1); + ErrorCode result = client->sendPacket(packet); + EXPECT_EQ(result, ErrorCode::SUCCESS); } +// Test sendPacket failure - not connected TEST_F(ClientTest, SendPacketFailureNotConnected) { Packet packet; - int result = client->sendPacket(packet); - EXPECT_EQ(result, -1); + ErrorCode result = client->sendPacket(packet); + EXPECT_EQ(result, ErrorCode::CONNECTION_FAILED); +} + +// Test sendPacket failure - socket send fails +TEST_F(ClientTest, SendPacketFailureOnSend) { + Packet packet; + client->connectToServer(1); + EXPECT_CALL(mockSocket, send(_, _, _, _)).WillOnce(Return(-1)); + ErrorCode result = client->sendPacket(packet); + EXPECT_EQ(result, ErrorCode::SEND_FAILED); +} + +// Test sendPacket partial send +TEST_F(ClientTest, SendPacketPartialSend) { + Packet packet; + client->connectToServer(1); + EXPECT_CALL(mockSocket, send(_, _, _, _)).WillOnce(Return(sizeof(Packet) - 1)); + ErrorCode result = client->sendPacket(packet); + EXPECT_EQ(result, ErrorCode::SEND_FAILED); +} + +// Test receivePacket success +TEST_F(ClientTest, ReceivePacketSuccess) { + Packet packet; + EXPECT_CALL(mockSocket, recv(_, _, _, _)).WillOnce(Return(sizeof(Packet))); + client->connectToServer(1); + std::thread receiveThread(&Client::receivePacket, client); + receiveThread.join(); + // If passPacketCom works, this could be tested, but for now we'll assume correct handling } -TEST_F(ClientTest, CloseConnection) { - client->closeConnection(); +// Test receivePacket failure (recv fails) +TEST_F(ClientTest, ReceivePacketFailure) { + EXPECT_CALL(mockSocket, recv(_, _, _, _)).WillOnce(Return(-1)); + client->connectToServer(1); + std::thread receiveThread(&Client::receivePacket, client); + receiveThread.join(); EXPECT_FALSE(client->isConnected()); } + +// Test receivePacket no data (recv returns 0) +TEST_F(ClientTest, ReceivePacketNoData) { + EXPECT_CALL(mockSocket, recv(_, _, _, _)).WillOnce(Return(0)); + client->connectToServer(1); + std::thread receiveThread(&Client::receivePacket, client); + receiveThread.join(); + EXPECT_TRUE(client->isConnected()); // Should still be connected, no data means wait for more +} + +// Test closeConnection success +TEST_F(ClientTest, CloseConnectionSuccess) { + client->connectToServer(1); + ErrorCode result = client->closeConnection(); + EXPECT_EQ(result, ErrorCode::SUCCESS); + EXPECT_FALSE(client->isConnected()); +} + +// Test closeConnection failure +TEST_F(ClientTest, CloseConnectionFailure) { + EXPECT_CALL(mockSocket, close(_)).WillOnce(Return(-1)); + ErrorCode result = client->closeConnection(); + EXPECT_EQ(result, ErrorCode::CLOSE_FAILED); +} + +// Test setCallback throws on null function +TEST_F(ClientTest, SetCallbackThrowsOnNull) { + EXPECT_THROW(client->setCallback(nullptr), std::invalid_argument); +} + +// Test setSocketInterface throws on null +TEST_F(ClientTest, SetSocketInterfaceThrowsOnNull) { + EXPECT_THROW(client->setSocketInterface(nullptr), std::invalid_argument); +} + +// Test valid socket interface setting +TEST_F(ClientTest, SetSocketInterfaceSuccess) { + MockSocket anotherMockSocket; + EXPECT_NO_THROW(client->setSocketInterface(&anotherMockSocket)); +} + +// Test connection thread starts correctly +TEST_F(ClientTest, ConnectionStartsReceiveThread) { + client->connectToServer(1); + EXPECT_TRUE(client->isReceiveThreadRunning()); +} + +// Test destructor closes connection +TEST_F(ClientTest, DestructorClosesConnection) { + client->connectToServer(1); + delete client; // Should close connection in destructor + EXPECT_CALL(mockSocket, close(_)).Times(1); // Ensure close is called +} + +// Test connection failure after sending a partial packet +TEST_F(ClientTest, SendPacketPartialConnectionClose) { + Packet packet; + client->connectToServer(1); + EXPECT_CALL(mockSocket, send(_, _, _, _)).WillOnce(Return(sizeof(Packet) - 1)); + ErrorCode result = client->sendPacket(packet); + EXPECT_EQ(result, ErrorCode::SEND_FAILED); + EXPECT_FALSE(client->isConnected()); // Should close connection after failure +} diff --git a/tests/server_test.cpp b/tests/server_test.cpp index 0beaca2c..61e7c5e5 100644 --- a/tests/server_test.cpp +++ b/tests/server_test.cpp @@ -1,44 +1,261 @@ - #include #include -#include "../src/server.h" -#include "../sockets/mock_socket.h" -#include "../src/message.h" +#include "../communication/src/server.h" +#include "../communication/sockets/mock_socket.h" +#include "../communication/src/message.h" using ::testing::_; using ::testing::Return; class ServerTest : public ::testing::Test { protected: - MockSocket mockSocket; - Server *server; + MockSocket* mockSocket; + Server* server; + int testPort = 8080; + Packet testPacket; void SetUp() override { - - ON_CALL(mockSocket, socket(::testing::_, ::testing::_, ::testing::_)) - .WillByDefault(::testing::Return(1)); - ON_CALL(mockSocket, setsockopt(::testing::_, ::testing::_, ::testing::_, ::testing::NotNull(), ::testing::_)) - .WillByDefault(::testing::Return(0)); - ON_CALL(mockSocket, bind(::testing::_, ::testing::_, ::testing::_)) - .WillByDefault(::testing::Return(0)); - ON_CALL(mockSocket, listen(::testing::_, ::testing::_)) - .WillByDefault(::testing::Return(0)); - ON_CALL(mockSocket, accept(_, _, _)) - .WillByDefault(::testing::Return(0)); - ON_CALL(mockSocket, close(_)) - .WillByDefault(::testing::Return(0)); - Packet packet; - ON_CALL(mockSocket, send(_, _, _, _)) - .WillByDefault(::testing::Return(sizeof(Packet))); - - server= new Server(1, [](void*){}, &mockSocket); + mockSocket = new MockSocket(); + server = new Server(testPort, [](Packet& packet) {}, mockSocket); } + void TearDown() override { - // delete client; + delete server; } }; -TEST_F(ServerTest, ConstructorCreatesSocket) { - EXPECT_GT(server->getServerSocket(), 0); +// Test for successful startConnection +TEST_F(ServerTest, StartConnection_Success) { + EXPECT_CALL(*mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(Return(3)); // Return a valid socket fd + + EXPECT_CALL(*mockSocket, setsockopt(3, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, _, sizeof(int))) + .WillOnce(Return(0)); + + EXPECT_CALL(*mockSocket, bind(3, _, sizeof(sockaddr_in))) + .WillOnce(Return(0)); + + EXPECT_CALL(*mockSocket, listen(3, 5)) + .WillOnce(Return(0)); + + ErrorCode result = server->startConnection(); + EXPECT_EQ(result, ErrorCode::SUCCESS); +} + +// Test for socket creation failure +TEST_F(ServerTest, StartConnection_SocketFailed) { + EXPECT_CALL(*mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(Return(-1)); // Simulate socket creation failure + + ErrorCode result = server->startConnection(); + EXPECT_EQ(result, ErrorCode::SOCKET_FAILED); +} + +// Test for bind failure +TEST_F(ServerTest, StartConnection_BindFailed) { + EXPECT_CALL(*mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(Return(3)); // Return a valid socket fd + + EXPECT_CALL(*mockSocket, setsockopt(3, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, _, sizeof(int))) + .WillOnce(Return(0)); + + EXPECT_CALL(*mockSocket, bind(3, _, sizeof(sockaddr_in))) + .WillOnce(Return(-1)); // Simulate bind failure + + + EXPECT_CALL(*mockSocket, close(3)).Times(1); // Expect close call due to failure + + ErrorCode result = server->startConnection(); + EXPECT_EQ(result, ErrorCode::BIND_FAILED); +} + +// Test for listen failure +TEST_F(ServerTest, StartConnection_ListenFailed) { + EXPECT_CALL(*mockSocket, socket(AF_INET, SOCK_STREAM, 0)) + .WillOnce(Return(3)); // Return a valid socket fd + + EXPECT_CALL(*mockSocket, setsockopt(3, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, _, sizeof(int))) + .WillOnce(Return(0)); + + EXPECT_CALL(*mockSocket, bind(3, _, sizeof(sockaddr_in))) + .WillOnce(Return(0)); + + EXPECT_CALL(*mockSocket, listen(3, 5)) + .WillOnce(Return(-1)); // Simulate listen failure + + EXPECT_CALL(*mockSocket, close(3)).Times(1); // Expect close call due to failure + + ErrorCode result = server->startConnection(); + EXPECT_EQ(result, ErrorCode::LISTEN_FAILED); +} + +// Test for successful sendDestination +TEST_F(ServerTest, SendDestination_Success) { + int clientSocket = 5; + testPacket.header.DestID = 1; + + // Simulate successful send operation + EXPECT_CALL(*mockSocket, send(clientSocket, &testPacket, sizeof(Packet), 0)) + .WillOnce(Return(sizeof(Packet))); + + { + std::lock_guard lock(*server->getIDMapMutex()); + (*server->getClientIDMap())[clientSocket] = testPacket.header.DestID; + } + + ErrorCode result = server->sendDestination(testPacket); + EXPECT_EQ(result, ErrorCode::SUCCESS); +} + +// Test for sendDestination failure +TEST_F(ServerTest, SendDestination_Failed) { + int clientSocket = 5; + testPacket.header.DestID = 1; + + // Simulate send failure + EXPECT_CALL(*mockSocket, send(clientSocket, &testPacket, sizeof(Packet), 0)) + .WillOnce(Return(0)); + + { + std::lock_guard lock(*server->getIDMapMutex()); + (*server->getClientIDMap())[clientSocket] = testPacket.header.DestID; + } + + ErrorCode result = server->sendDestination(testPacket); + EXPECT_EQ(result, ErrorCode::SEND_FAILED); +} + +// Test for sendDestination failure +TEST_F(ServerTest, SendDestination_Connection_Failed) { + int clientSocket = 5; + testPacket.header.DestID = 1; + + // Simulate send failure + EXPECT_CALL(*mockSocket, send(clientSocket, &testPacket, sizeof(Packet), 0)) + .WillOnce(Return(-1)); + + { + std::lock_guard lock(*server->getIDMapMutex()); + (*server->getClientIDMap())[clientSocket] = testPacket.header.DestID; + } + + ErrorCode result = server->sendDestination(testPacket); + EXPECT_EQ(result, ErrorCode::CONNECTION_FAILED); +} + +// Test for sending message to an invalid client ID +TEST_F(ServerTest, SendDestination_InvalidClientID) { + testPacket.header.DestID = 999; // Invalid client ID + ErrorCode result = server->sendDestination(testPacket); + EXPECT_EQ(result, ErrorCode::INVALID_CLIENT_ID); // Ensure failure for invalid client ID +} + +// Test for send failure when sending to a client +TEST_F(ServerTest, SendDestination_SendFailed) { + testPacket.header.DestID = 1; // Valid client ID + int clientSocket = 3; + EXPECT_CALL(*mockSocket, send(clientSocket, _, sizeof(Packet), 0)) + .WillOnce(Return(0)); // Simulate send failure + + // EXPECT_CALL(*mockSocket, close(clientSocket)); // Close socket on failure + + { + std::lock_guard lock(*server->getIDMapMutex()); + (*server->getClientIDMap())[clientSocket] = testPacket.header.DestID; // Map client to ID + } + + ErrorCode result = server->sendDestination(testPacket); + EXPECT_EQ(result, ErrorCode::SEND_FAILED); // Ensure correct error code on send failure +} + +// Test for closing an invalid socket +TEST_F(ServerTest, CloseInvalidSocket) { + int invalidSocket = -1; + + EXPECT_CALL(*mockSocket, close(invalidSocket)) + .Times(0); // No close call since socket is invalid + + server->stopServer(); + EXPECT_EQ(server->testGetClientSocketByID(999), -1); // Ensure client does not exist +} + +// Test for successful broadcast +TEST_F(ServerTest, SendBroadcast_Success) { + int clientSocket1 = 3; + int clientSocket2 = 4; + + // Simulate successful broadcast to two clients + EXPECT_CALL(*mockSocket, send(clientSocket1, _, sizeof(Packet), 0)) + .WillOnce(Return(sizeof(Packet))); // Success for client 1 + + EXPECT_CALL(*mockSocket, send(clientSocket2, _, sizeof(Packet), 0)) + .WillOnce(Return(sizeof(Packet))); // Success for client 2 + + { + std::lock_guard lock(*server->getSocketMutex()); + (*server->getSockets()).push_back(clientSocket1); // Add client 1 + (*server->getSockets()).push_back(clientSocket2); // Add client 2 + } + + ErrorCode result = server->sendBroadcast(testPacket); + EXPECT_EQ(result, ErrorCode::SUCCESS); // Ensure broadcast was successful } +// Test for broadcast failure +TEST_F(ServerTest, SendBroadcast_SendFailed) { + int clientSocket1 = 3; + int clientSocket2 = 4; + + EXPECT_CALL(*mockSocket, send(clientSocket1, _, sizeof(Packet), 0)) + .WillOnce(Return(sizeof(Packet))); // Success for client 1 + + EXPECT_CALL(*mockSocket, send(clientSocket2, _, sizeof(Packet), 0)) + .WillOnce(Return(-1)); // Failure for client 2 + + //EXPECT_CALL(*mockSocket, close(clientSocket2)); // Close socket on failure + + { + std::lock_guard lock(*server->getSocketMutex()); + (*server->getSockets()).push_back(clientSocket1); // Add client 1 + (*server->getSockets()).push_back(clientSocket2); // Add client 2 + } + + ErrorCode result = server->sendBroadcast(testPacket); + EXPECT_EQ(result, ErrorCode::CONNECTION_FAILED); // Ensure broadcast failure is handled +} + +// Test for handling client disconnection during message reception +TEST_F(ServerTest, HandleClient_Disconnection) { + int clientSocket = 5; + + EXPECT_CALL(*mockSocket, recv(clientSocket, _, sizeof(Packet), 0)) + .WillOnce(Return(0)); // Simulate client disconnection + + //EXPECT_CALL(*mockSocket, close(clientSocket)); // Close socket for disconnected client + + server->testHandleClient(clientSocket); + std::vector* sockets = server->getSockets(); + + { + std::lock_guard lock(*server->getSocketMutex()); + EXPECT_EQ(std::find(sockets->begin(), sockets->end(), clientSocket), sockets->end()); + } +} + +// Test for receive failure from a client +TEST_F(ServerTest, HandleClient_ReceiveFailed) { + int clientSocket = 5; + + EXPECT_CALL(*mockSocket, recv(clientSocket, _, sizeof(Packet), 0)) + .WillOnce(Return(-1)); // Simulate receive failure + + //EXPECT_CALL(*mockSocket, close(clientSocket)); // Close socket on failure + + server->testHandleClient(clientSocket); + + std::vector* sockets = server->getSockets(); + { + std::lock_guard lock(*server->getSocketMutex()); + EXPECT_EQ(std::find(sockets->begin(), sockets->end(), clientSocket), sockets->end()); + } +} \ No newline at end of file From bf8625a074fb06ad39d231066e736fedb14e1f82 Mon Sep 17 00:00:00 2001 From: ayala-freind <0527688937a@gmail.com> Date: Thu, 15 Aug 2024 11:56:27 +0300 Subject: [PATCH 11/18] logger with basic tests --- CMakeLists.txt | 55 ++++++++++++++++++++++++++++++++++++++++++++- test/testLogger.cpp | 51 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 test/testLogger.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 754bf7c9..9a6058a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,27 +1,80 @@ cmake_minimum_required(VERSION 3.10) project(basicApi) + +# FetchContent for GoogleTest and GoogleMock +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) + +FetchContent_Declare( + googlemock + GIT_REPOSITORY https://github.com/google/googlemock.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googlemock) + # Set C++ standard set(CMAKE_CXX_STANDARD 17) + # Add source files set(SOURCES src/Packet.cpp src/communication.cpp test/testCommunication.cpp ) + # Include directories +include_directories(${gtest_SOURCE_DIR}/include ${gmock_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/logger) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/sockets) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/test) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../googletest/googletest/include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../googletest/googlemock/include) + +# Add the logger source files +set(LOGGER_SOURCES + ${CMAKE_SOURCE_DIR}/logger/logger.cpp +) + +# Add the test source files +set(TEST_SOURCES + ${CMAKE_SOURCE_DIR}/test/loggerTest.cpp +) + # Find and link pthread find_package(Threads REQUIRED) + # Add GTest and GMock add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../googletest googletest-build) + +# Define LOG_LEVEL for compilation (default: INFO) +set(DEFAULT_LOG_LEVEL "logger::LogLevel::INFO") +set(LOG_LEVEL ${DEFAULT_LOG_LEVEL} CACHE STRING "Set the logging level (e.g., logger::LogLevel::ERROR, logger::LogLevel::INFO, logger::LogLevel::DEBUG)") +add_definitions(-DLOG_LEVEL=${LOG_LEVEL}) + # Add the test executable add_executable(testCommunication ${SOURCES}) + +# Create the test executable +add_executable(LoggerTests ${LOGGER_SOURCES} ${TEST_SOURCES}) + +# Link the GTest and GMock libraries +target_link_libraries(LoggerTests + gtest + gmock + pthread +) target_link_libraries(testCommunication gtest gmock gtest_main gmock_main Threads::Threads) + # Enable testing enable_testing() + +# Ensure the test executable is built +add_test(NAME LoggerTests COMMAND LoggerTests) + # Add tests -add_test(NAME testCommunication COMMAND testCommunication) \ No newline at end of file +add_test(NAME testCommunication COMMAND testCommunication) diff --git a/test/testLogger.cpp b/test/testLogger.cpp new file mode 100644 index 00000000..2e3345e5 --- /dev/null +++ b/test/testLogger.cpp @@ -0,0 +1,51 @@ +#include +#include +#include "logger.h" + +using ::testing::Return; + +class MockLogger : public logger { +public: + MOCK_METHOD(void, logMessage, (LogLevel level, const std::string &message), (override)); +}; + +class LoggerTest : public ::testing::Test { +protected: + LoggerTest() { + logger::initializeLogFile(); + } + + void SetUp() override { + std::remove("test.log"); // Ensure the file does not exist before the test + } + + void TearDown() override { + std::remove("test.log"); // Clean up after the test + } +}; + +TEST_F(LoggerTest, InitializeLogFile) { + std::ifstream logFile(logger::getLogFileName()); + EXPECT_TRUE(logFile.good()) << "Log file was not created: " << logger::getLogFileName(); + logFile.close(); +} + +TEST_F(LoggerTest, LogMessage) { + logger::logMessage(logger::LogLevel::INFO, "Test INFO message"); + logger::logMessage(logger::LogLevel::ERROR, "Test ERROR message"); + + std::ifstream logFile(logger::getLogFileName()); + std::string content((std::istreambuf_iterator(logFile)), std::istreambuf_iterator()); + EXPECT_NE(content.find("Test INFO message"), std::string::npos); + EXPECT_NE(content.find("Test ERROR message"), std::string::npos); +} + +TEST_F(LoggerTest, LogLevelFiltering) { + logger::logMessage(logger::LogLevel::ERROR, "Test ERROR message"); + logger::logMessage(logger::LogLevel::INFO, "Test INFO message"); + + std::ifstream logFile(logger::getLogFileName()); + std::string content((std::istreambuf_iterator(logFile)), std::istreambuf_iterator()); + EXPECT_NE(content.find("Test ERROR message"), std::string::npos); + EXPECT_EQ(content.find("Test INFO message"), std::string::npos) << "INFO message should not be logged with ERROR level"; +} From ac8a881ca1f59e8e40e5c9f6ec508de77f570cd2 Mon Sep 17 00:00:00 2001 From: Shoshi Date: Mon, 30 Sep 2024 18:49:35 +0300 Subject: [PATCH 12/18] Add global clock to CAN BUS simulation --- communication/include/client_connection.h | 1 + communication/include/global_clock.h | 132 ++++++++++++++++++ communication/src/global_clock.cpp | 155 ++++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 communication/include/global_clock.h create mode 100644 communication/src/global_clock.cpp diff --git a/communication/include/client_connection.h b/communication/include/client_connection.h index f72d0bf0..3e31c863 100644 --- a/communication/include/client_connection.h +++ b/communication/include/client_connection.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "message.h" #include "../sockets/Isocket.h" #include "../sockets/mock_socket.h" diff --git a/communication/include/global_clock.h b/communication/include/global_clock.h new file mode 100644 index 00000000..7ced3b6f --- /dev/null +++ b/communication/include/global_clock.h @@ -0,0 +1,132 @@ +#ifndef __GLOBAL_CLOCK_H__ +#define __GLOBAL_CLOCK_H__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SHM_NAME "/clock__shm" +#define TICK_DURATION std::chrono::milliseconds(50) // Define the tick duration +#define MAX_TICK std::numeric_limits::max() // Define the maximum value for the tick + +/** + * @struct SharedClock + * @brief A structure representing the shared state of the global clock. + * + * This structure is stored in shared memory, allowing multiple processes to access and modify + * the clock state. It contains: + * - `current_tick`: The current value of the clock tick. + * - `is_running`: A boolean flag indicating whether the clock is running. + * - `tick_mutex`: A mutex used to protect access to the tick count. + * - `tick_cond`: A condition variable used to notify waiting processes when a tick occurs. + */ +struct SharedClock { + std::atomic current_tick; ///< The current tick value. + std::atomic is_running; ///< Whether the clock is currently running. + pthread_mutex_t tick_mutex; ///< Mutex to synchronize access to the tick count. + pthread_cond_t tick_cond; ///< Condition variable to notify waiting threads on tick change. +}; +/** + * @class GlobalClock + * @brief A class that manages a global clock accessible across multiple processes using shared memory. + * + * The `GlobalClock` class is responsible for maintaining a synchronized global clock that increments + * ticks at regular intervals. This clock can be shared across multiple processes using shared memory, + * and synchronization is achieved using mutexes and condition variables to ensure that processes can + * wait for the next tick and query the current tick in a thread-safe manner. + * + * The clock can be started and stopped, and it ensures proper cleanup of shared resources when the + * process is terminated. + */ +class GlobalClock { + public: + /** + * @brief Starts the global clock. + * + * Initializes shared memory if not already initialized, sets the clock to running, + * and begins ticking at regular intervals in a separate thread. Only one process can start the clock. + */ + static void startClock(); + /** + * @brief Waits for the next tick. + * + * Blocks the calling thread until the next clock tick occurs, allowing synchronization + * with the global clock. Uses condition variables to wait for the clock tick to change. + */ + static void waitForNextTick(); + /** + * @brief Retrieves the current tick value. + * + * @return The current value of the clock tick, as an integer. + * + * Ensures the shared memory is initialized, and returns the current tick count stored in + * the shared memory. + */ + static int getCurrentTick(); + /** + * @brief Stops the global clock. + * + * Sets the `is_running` flag in the shared memory to `false`, stopping the clock and + * ensuring that the clock thread is properly joined before exiting. + */ + static void stopClock(); + + private: + /** + * @brief Initializes shared memory for the global clock if not already initialized for this process. + * + * Allocates shared memory and initializes the `SharedClock` structure if necessary, which contains the + * current tick, running state, mutex, and condition variable. + */ + static void initializeSharedMemory(); + /** + * @brief .Initializes the mutex and condition variables for shared clock synchronization. + * + * This function is only called by the first process that starts the clock. + * It sets the mutex and condition variable to be shared between processes + * using `PTHREAD_PROCESS_SHARED`. + */ + static void initializeClockSynchronization(); + /** + * @brief Releases process-specific resources. + * + * Unmaps the shared memory and closes the file descriptor associated with it. + * This method ensures that the process-specific resources are properly cleaned up. + */ + static void releaseProcessResources(); + /** + * @brief Releases system-wide shared resources. + * + * Unlinks the shared memory, effectively deleting it from the system, and closes the file + * descriptor. This should be called when no other process requires access to the clock. + */ + static void releaseSharedSystemResources(); + /** + * @brief Registers a cleanup routine. + * + * Registers a cleanup routine using `atexit()` to ensure that when the process exits, it + * properly releases its resources, whether those are process-specific or system-wide. + */ + static void registerCleanup(); + /** + * @brief The main function to run the clock. + * + * Increments the tick count in regular intervals (defined by `TICK_DURATION`) while the + * clock is running. Broadcasts the condition variable on each tick to wake up waiting processes. + */ + static void runClock(); + // Shared memory for the clock state, accessible across processes. + static SharedClock *shared_clock; + // File descriptor for the shared memory. + static int shared_memory_fd; + // Thread to manage the clock in the background. + static std::thread clock_thread; +}; +#endif // __GLOBAL_CLOCK_H__ \ No newline at end of file diff --git a/communication/src/global_clock.cpp b/communication/src/global_clock.cpp new file mode 100644 index 00000000..30bb20e9 --- /dev/null +++ b/communication/src/global_clock.cpp @@ -0,0 +1,155 @@ +#include "../include/global_clock.h" + +SharedClock *GlobalClock::shared_clock = nullptr; +int GlobalClock::shared_memory_fd = -1; +std::thread GlobalClock::clock_thread; + +void GlobalClock::startClock() +{ + initializeSharedMemory(); // Initialize shared memory if not done yet + if (shared_clock->is_running + .load()) { // Check if the clock is already running + std::cerr << "Clock is already running." << std::endl; + return; // Exit if clock is running + } + initializeClockSynchronization(); //Initialize the mutex and condition variables for shared clock synchronization. + shared_clock->is_running.store(true); // Set the clock state to running + clock_thread = std::thread(runClock); // Run the clock in a new thread +} + +int GlobalClock::getCurrentTick() +{ + initializeSharedMemory(); // Ensure shared memory is initialized before accessing the tick + return shared_clock->current_tick.load(); // Return the current tick value +} + +void GlobalClock::waitForNextTick() +{ + initializeSharedMemory(); // Ensure shared memory is initialized + pthread_mutex_lock( + &shared_clock->tick_mutex); // Lock the mutex to access the tick safely + pthread_cond_wait( + &shared_clock->tick_cond, + &shared_clock->tick_mutex); // Wait for tick condition signal + pthread_mutex_unlock( + &shared_clock->tick_mutex); // Unlock the mutex after tick change +} + +void GlobalClock::stopClock() +{ + if (shared_clock->is_running.load()) { + shared_clock->is_running.store(false); // Stop the clock + if (clock_thread.joinable()) { + clock_thread.join(); // Wait for the clock thread to finish + } + } +} + +void GlobalClock::initializeSharedMemory() +{ + if (shared_clock == + nullptr) { // Check if shared memory is already initialized + shared_memory_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, + 0666); // Open shared memory file descriptor + if (shared_memory_fd == -1) { + std::cerr << "Error accessing shared memory." << std::endl; + exit(1); // Exit if there's an error accessing shared memory + } + ftruncate(shared_memory_fd, + sizeof(SharedClock)); // Set size of shared memory + shared_clock = static_cast( + mmap(0, sizeof(SharedClock), PROT_READ | PROT_WRITE, MAP_SHARED, + shared_memory_fd, 0)); // Map shared memory + if (shared_clock == MAP_FAILED) { + std::cerr << "Error mapping shared memory." << std::endl; + exit(1); // Exit if there's an error mapping shared memory + } + registerCleanup(); // Register cleanup function to be called at exit + } +} + +void GlobalClock::initializeClockSynchronization() +{ + // Initialize mutex and condition variables for shared clock synchronization + pthread_mutexattr_t mutex_attr; + pthread_condattr_t cond_attr; + + // Initialize the mutex attribute and set it to be shared between processes + pthread_mutexattr_init(&mutex_attr); + pthread_mutexattr_setpshared( + &mutex_attr, + PTHREAD_PROCESS_SHARED); // Set mutex to be shared between processes + pthread_mutex_init(&shared_clock->tick_mutex, + &mutex_attr); // Initialize mutex in shared memory + + // Initialize the condition attribute and set it to be shared between processes + pthread_condattr_init(&cond_attr); + pthread_condattr_setpshared( + &cond_attr, + PTHREAD_PROCESS_SHARED); // Set condition variable to be shared between processes + pthread_cond_init( + &shared_clock->tick_cond, + &cond_attr); // Initialize condition variable in shared memory +} + +void GlobalClock::releaseProcessResources() +{ + std::cout << "Releasing process-specific resources..." << std::endl; + if (shared_clock != nullptr) { + munmap( + shared_clock, + sizeof( + SharedClock)); // Unmap the shared memory from the process address space + close(shared_memory_fd); // Close the file descriptor for shared memory + shared_clock = nullptr; // Reset pointer to shared clock + shared_memory_fd = -1; // Reset shared memory file descriptor + } +} + +void GlobalClock::releaseSharedSystemResources() +{ + std::cout << "Releasing system-wide shared resources..." << std::endl; + if (shared_memory_fd != -1) { + shm_unlink( + SHM_NAME); // Unlink shared memory from the system (delete it) + close(shared_memory_fd); // Close the file descriptor for shared memory + shared_memory_fd = -1; // Reset shared memory file descriptor + } +} + +void GlobalClock::registerCleanup() +{ + atexit([]() { // Register a cleanup function to be called when the process exits + if (shared_clock->is_running.load()) { + GlobalClock:: + releaseProcessResources(); // Release process-specific resources if the clock is running + } + else { + GlobalClock:: + releaseSharedSystemResources(); // Release system-wide resources if the clock is not running + } + std::cout << "Cleanup registered." << std::endl; + }); +} + +void GlobalClock::runClock() +{ + while (shared_clock->is_running.load()) { + std::this_thread::sleep_for(TICK_DURATION); // Sleep for tick duration + pthread_mutex_lock( + &shared_clock + ->tick_mutex); // Lock the mutex to safely increment the tick + // Increment the tick and check for overflow + if (shared_clock->current_tick.load() >= MAX_TICK) { + shared_clock->current_tick.store(0); // Reset to 0 if max is reached + } + else { + shared_clock->current_tick++; // Increment the tick + } + pthread_cond_broadcast( + &shared_clock + ->tick_cond); // Notify all waiting processes that the tick has changed + pthread_mutex_unlock( + &shared_clock->tick_mutex); // Unlock the mutex after tick change + } +} \ No newline at end of file From e117cb68d503124372785540b6a2bf5de9f4aa98 Mon Sep 17 00:00:00 2001 From: Shoshi Date: Tue, 1 Oct 2024 11:36:11 +0300 Subject: [PATCH 13/18] Update packet to comply with CAN BUS protocol --- communication/include/bus_manager.h | 6 +- communication/include/communication.h | 9 +- communication/include/message.h | 40 ++++++-- communication/include/packet.h | 114 +++++++++++++--------- communication/sockets/real_socket.cpp | 18 ++-- communication/src/bus_manager.cpp | 12 +-- communication/src/communication.cpp | 29 +++--- communication/src/message.cpp | 64 +++++++++--- communication/src/packet.cpp | 124 +++++++++++++++++++----- communication/src/server_connection.cpp | 4 +- 10 files changed, 290 insertions(+), 130 deletions(-) diff --git a/communication/include/bus_manager.h b/communication/include/bus_manager.h index 74f0beec..fa00bc0c 100644 --- a/communication/include/bus_manager.h +++ b/communication/include/bus_manager.h @@ -27,6 +27,9 @@ class BusManager // Sends to the server to listen for requests ErrorCode startConnection(); + // Stops server connection + static void stopConnection(); + // Receives the packet that arrived and checks it before sending it out void receiveData(Packet &p); @@ -36,8 +39,5 @@ class BusManager // Implement a priority check according to the CAN bus Packet packetPriority(Packet &a, Packet &b); - // Static method to handle SIGINT signal - static void signalHandler(int signum); - ~BusManager(); }; \ No newline at end of file diff --git a/communication/include/communication.h b/communication/include/communication.h index 5485abae..79ce295a 100644 --- a/communication/include/communication.h +++ b/communication/include/communication.h @@ -51,10 +51,15 @@ class Communication ErrorCode startConnection(); // Sends a message to manager - ErrorCode sendMessage(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, bool isBroadcast); + ErrorCode sendMessage(void *data, size_t dataSize, uint32_t destID, + uint32_t srcID, + MessageType messageType = MessageType::DATA_MESSAGE); // Sends a message to manager - Async - void sendMessageAsync(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, std::function passSend, bool isBroadcast); + void sendMessageAsync(void *data, size_t dataSize, uint32_t destID, + uint32_t srcID, + std::function sendCallback, + MessageType messageType = MessageType::DATA_MESSAGE); //Destructor ~Communication(); diff --git a/communication/include/message.h b/communication/include/message.h index e81ce9d8..5916e4f9 100644 --- a/communication/include/message.h +++ b/communication/include/message.h @@ -6,21 +6,35 @@ #include #include #include +#include #include "packet.h" -class Message -{ +enum MessageType { + INITIAL_CONNECTION, // Represents initial connection of a component to the server + ERROR_MESSAGE, // Error message (highest priority) + ACK, // Acknowledgment message + OVERLOAD_MESSAGE, // Indicates network overload + REMOTE_TRANSMISSION_REQUEST, // Request for remote data (RTR) + DATA_MESSAGE // Regular data message (lowest priority) +}; + + + +class Message { private: - std::vector packets; - uint32_t tps; - + std::vector packets; + uint32_t tps; + uint32_t messageID; + + uint32_t generateMessageID(MessageType messageType, uint16_t srcID); + public: - // Default + // Default constructor Message() = default; // Constructor for sending message - Message(uint32_t srcID, void *data, int dlc, bool isBroadcast, uint32_t destID = 0xFFFF); - + Message(uint32_t srcID, uint32_t destID, void* data, size_t size, MessageType messageType); + // Constructor for receiving message Message(uint32_t tps); @@ -31,8 +45,14 @@ class Message bool isComplete() const; // Get the complete data of the message - void *completeData() const; + void* completeData() const; // Get the packets of the message - std::vector &getPackets(); + std::vector& getPackets(); + + // Extract the MessageType from the message ID + static MessageType getMessageTypeFromID(uint32_t messageID); + + // Returns a string representation of the MessageType enum value + static std::string getMessageTypeString(MessageType type); }; \ No newline at end of file diff --git a/communication/include/packet.h b/communication/include/packet.h index 25391d3e..4e4947ae 100644 --- a/communication/include/packet.h +++ b/communication/include/packet.h @@ -1,49 +1,75 @@ -#pragma once -#include +#ifndef __PACKET_H__ +#define __PACKET_H__ + #include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include +#include + #define SIZE_PACKET 8 -class Packet -{ + +class Packet { public: - // Packet header containing various metadata fields - struct Header - { - uint32_t ID; // Message ID - uint32_t PSN; // Packet Sequence Number - uint32_t TPS; // Total Packet Sum - uint32_t SrcID; // Source ID - uint32_t DestID; // Destination ID - uint8_t DLC; // Data Length Code (0-8 bits) - uint16_t CRC; // Cyclic Redundancy Check for error detection - int timestamp; // Timestamp field - bool isBroadcast; // True for broadcast, false for unicas - bool passive; - bool RTR; - } header; - - void *data[SIZE_PACKET]; - - // Default constructor for Packet. - Packet() = default; - - // Constructor for sending message - Packet(uint32_t id, uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, void *data, uint8_t dlc, bool isBroadcast, bool RTR = false, bool passive = false); - - // Constructor for receiving message - Packet(uint32_t id); - - // Calculate CRC for the given data and length - uint16_t calculateCRC(const void *data, size_t length); - - // A function to convert the data to hexa (logger) - std::string pointerToHex(const void *ptr, size_t size) const; + // Default constructor for Packet. + Packet() = default; + + // Constructor for sending message + Packet(uint32_t id, uint32_t PSN, uint32_t TPS, uint32_t srcId, + uint32_t destId, int DLC, bool RTR, bool isBroadcast, + const uint8_t *payload); + + // Constructor for receiving message + Packet(uint32_t id); + + // Destructor + ~Packet() = default; + + // Overloaded operator> + bool operator>(const Packet &other) const; + + // Getters for accessing Header fields + uint32_t getId() const { return header.id; } + uint32_t getPSN() const { return header.PSN; } + uint32_t getTPS() const { return header.TPS; } + uint32_t getSrcId() const { return header.srcId; } + uint32_t getDestId() const { return header.destId; } + int getTimestamp() const { return header.timestamp; } + int getDLC() const { return header.DLC; } + uint16_t getCRC() const { return header.CRC; } + bool isRTR() const { return header.RTR; } + bool getIsPassive() const { return header.passive; } + bool getIsBroadcast() const { return header.isBroadcast; } + + const uint8_t *getPayload() const { return payload; } + + // Setters for modifying Header fields + void setIsPassive(bool p) { header.passive = p; } + void setTimestamp(int t) { header.timestamp = t; } + + // CRC calculation + uint16_t calculateCRC() const; + bool validateCRC() const; + + // A function to convert the data to hexa (logger) + std::string pointerToHex() const; + +private: + // Header structure within Packet + struct Header { + uint32_t id; // Unique identifier for the message (4 bytes) + uint32_t PSN; // Packet Sequence Number (4 bytes) + uint32_t TPS; // Total Packet Sum (4 bytes) + uint32_t srcId; // Source node ID (4 bytes) + uint32_t destId; // Destination node ID (4 bytes) + int timestamp; // Timestamp marking the packet's send time (4 bytes) + int DLC; // Data Length Code (0-8 bytes) (4 bytes) + uint16_t CRC; // Cyclic Redundancy Check (2 bytes) + bool RTR; // Remote Transmission Request flag (1 byte) + bool passive; // Passive state flag (1 byte) + bool isBroadcast; // Broadcast flag (1 byte) + } header; + + uint8_t payload[SIZE_PACKET]; // Data payload (fixed size, up to SIZE_PACKET bytes) }; + +#endif // __PACKET_H__ \ No newline at end of file diff --git a/communication/sockets/real_socket.cpp b/communication/sockets/real_socket.cpp index dda6f23f..530d33b0 100644 --- a/communication/sockets/real_socket.cpp +++ b/communication/sockets/real_socket.cpp @@ -83,14 +83,14 @@ ssize_t RealSocket::recv(int sockfd, void *buf, size_t len, int flags) const Packet *p = static_cast(buf); if (valread < 0) - RealSocket::log.logMessage(logger::LogLevel::ERROR, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string(" Error occurred: in socket ") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); + RealSocket::log.logMessage(logger::LogLevel::ERROR, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), std::string(" Error occurred: in socket ") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); else if (valread == 0) - RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string(" connection closed: in socket ") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), std::string(" connection closed: in socket ") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); else { - if (!p->header.DLC) - RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string("received packet number: ") + std::to_string(p->header.PSN) + std::string(", of messageId: ") + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno)) + " ID for connection: " + std::to_string(p->header.SrcID)); + if (!p->getDLC()) + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), std::string("received packet number: ") + std::to_string(p->getPSN()) + std::string(", of messageId: ") + std::to_string(p->getId()) + std::string(" ") + std::string(strerror(errno)) + " ID for connection: " + std::to_string(p->getSrcId())); else - RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), std::string("received packet number: ") + std::to_string(p->header.PSN) + std::string(", of messageId: ") + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno)) + " Data: " + p->pointerToHex(p->data,p->header.DLC)); + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), std::string("received packet number: ") + std::to_string(p->getPSN()) + std::string(", of messageId: ") + std::to_string(p->getId()) + std::string(" ") + std::string(strerror(errno)) + " Data: " + p->pointerToHex()); } @@ -103,12 +103,12 @@ ssize_t RealSocket::send(int sockfd, const void *buf, size_t len, int flags) const Packet *p = static_cast(buf); if (sendAns <= 0) { - RealSocket::log.logMessage(logger::LogLevel::ERROR, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), "sending packet number: " + std::to_string(p->header.PSN) + ", of messageId: " + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno))); + RealSocket::log.logMessage(logger::LogLevel::ERROR, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), "sending packet number: " + std::to_string(p->getPSN()) + ", of messageId: " + std::to_string(p->getId()) + std::string(" ") + std::string(strerror(errno))); } - if (!p->header.DLC) - RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), "sending packet number: " + std::to_string(p->header.PSN) + ", of messageId: " + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno)) + " ID for connection: " + std::to_string(p->header.SrcID)); + if (!p->getDLC()) + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), "sending packet number: " + std::to_string(p->getPSN()) + ", of messageId: " + std::to_string(p->getId()) + std::string(" ") + std::string(strerror(errno)) + " ID for connection: " + std::to_string(p->getSrcId())); else - RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->header.SrcID), std::to_string(p->header.DestID), "sending packet number: " + std::to_string(p->header.PSN) + ", of messageId: " + std::to_string(p->header.ID) + std::string(" ") + std::string(strerror(errno)) + " Data: " + p->pointerToHex(p->data,p->header.DLC)); + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), "sending packet number: " + std::to_string(p->getPSN()) + ", of messageId: " + std::to_string(p->getId()) + std::string(" ") + std::string(strerror(errno)) + " Data: " + p->pointerToHex()); return sendAns; } diff --git a/communication/src/bus_manager.cpp b/communication/src/bus_manager.cpp index 225be049..57fbd797 100644 --- a/communication/src/bus_manager.cpp +++ b/communication/src/bus_manager.cpp @@ -7,7 +7,6 @@ std::mutex BusManager::managerMutex; BusManager::BusManager(std::vector idShouldConnect, uint32_t limit) :server(8080, std::bind(&BusManager::receiveData, this, std::placeholders::_1))//,syncCommunication(idShouldConnect, limit) { // Setup the signal handler for SIGINT - signal(SIGINT, BusManager::signalHandler); } // Static function to return a singleton instance @@ -45,7 +44,7 @@ void BusManager::receiveData(Packet &p) // Sending according to broadcast variable ErrorCode BusManager::sendToClients(const Packet &packet) { - if(packet.header.isBroadcast) + if(packet.getIsBroadcast()) return server.sendBroadcast(packet); return server.sendDestination(packet); } @@ -59,16 +58,15 @@ Packet BusManager::checkCollision(Packet ¤tPacket) // Implement a priority check according to the CAN bus Packet BusManager::packetPriority(Packet &a, Packet &b) { - return (a.header.SrcID < b.header.SrcID) ? a : b; + return (a.getSrcId() < b.getSrcId()) ? a : b; } -// Static method to handle SIGINT signal -void BusManager::signalHandler(int signum) +//shut down the server +void BusManager::stopConnection() { if (instance) { - instance->server.stopServer(); // Call the stopServer method + instance->server.stopServer(); // Stop server on interrupt } - exit(signum); } BusManager::~BusManager() { diff --git a/communication/src/communication.cpp b/communication/src/communication.cpp index 3ec8ff70..3d716aaa 100644 --- a/communication/src/communication.cpp +++ b/communication/src/communication.cpp @@ -29,7 +29,9 @@ ErrorCode Communication::startConnection() } // Sends a message sync -ErrorCode Communication::sendMessage(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, bool isBroadcast) +ErrorCode Communication::sendMessage(void *data, size_t dataSize, uint32_t destID, + uint32_t srcID, + MessageType messageType) { if (dataSize == 0) return ErrorCode::INVALID_DATA_SIZE; @@ -40,10 +42,9 @@ ErrorCode Communication::sendMessage(void *data, size_t dataSize, uint32_t destI if (!client.isConnected()) return ErrorCode::CONNECTION_FAILED; - Message msg(srcID, data, dataSize, isBroadcast, destID); - + Message msg(srcID, destID, data, dataSize,messageType); // Create a new message //Sending the message to logger - RealSocket::log.logMessage(logger::LogLevel::INFO,std::to_string(srcID),std::to_string(destID),"Complete message:" + msg.getPackets().at(0).pointerToHex(data, dataSize)); + RealSocket::log.logMessage(logger::LogLevel::INFO,std::to_string(srcID),std::to_string(destID),"Complete message:" + msg.getPackets().at(0).pointerToHex()); for (auto &packet : msg.getPackets()) { ErrorCode res = client.sendPacket(packet); @@ -55,14 +56,16 @@ ErrorCode Communication::sendMessage(void *data, size_t dataSize, uint32_t destI } // Sends a message Async -void Communication::sendMessageAsync(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, std::function sendCallback, bool isBroadcast) +void Communication::sendMessageAsync(void *data, size_t dataSize, uint32_t destID, + uint32_t srcID, + std::function sendCallback, + MessageType messageType) { std::promise resultPromise; std::future resultFuture = resultPromise.get_future(); - std::thread([this, data, dataSize, destID, srcID, isBroadcast, &resultPromise]() { - ErrorCode res = this->sendMessage(data, dataSize, destID, srcID, isBroadcast); - resultPromise.set_value(res); + std::thread([this, data, dataSize, destID, srcID, messageType, sendCallback,&resultPromise]() { + ErrorCode result = this->sendMessage(data, dataSize, destID, srcID, messageType); // Send the message resultPromise.set_value(res); }).detach(); ErrorCode res = resultFuture.get(); @@ -83,13 +86,13 @@ void Communication::receivePacket(Packet &p) // Checks if the packet is intended for him bool Communication::checkDestId(Packet &p) { - return p.header.isBroadcast || p.header.DestID == this->id; + return p.getIsBroadcast() || p.getDestId() == this->id; } // Checks if the data is currect bool Communication::validCRC(Packet &p) { - return p.header.CRC == p.calculateCRC(p.data, p.header.DLC); + return p.validateCRC(); } // Receives the packet and adds it to the message @@ -117,13 +120,13 @@ Packet Communication::hadArrived() // Adding the packet to the complete message void Communication::addPacketToMessage(Packet &p) { - std::string messageId = std::to_string(p.header.ID); + std::string messageId = std::to_string(p.getId()); // If the message already exists, we will add the packet if (receivedMessages.find(messageId) != receivedMessages.end()) { receivedMessages[messageId].addPacket(p); } else { // If the message does not exist, we will create a new message - Message msg(p.header.TPS); + Message msg(p.getTPS()); msg.addPacket(p); receivedMessages[messageId] = msg; } @@ -131,7 +134,7 @@ void Communication::addPacketToMessage(Packet &p) // If the message is complete, we pass the data to the passData function if (receivedMessages[messageId].isComplete()) { void *completeData = receivedMessages[messageId].completeData(); - passData(p.header.SrcID, completeData); + passData(p.getSrcId(), completeData); receivedMessages.erase(messageId); // Removing the message once completed } } diff --git a/communication/src/message.cpp b/communication/src/message.cpp index 35da7a15..5f79cdb4 100644 --- a/communication/src/message.cpp +++ b/communication/src/message.cpp @@ -1,19 +1,34 @@ #include "../include/message.h" // Constructor for sending message -Message::Message(uint32_t srcID, void *data, int dlc, bool isBroadcast, uint32_t destID) +Message::Message(uint32_t srcID, uint32_t destID, void* data, size_t size, MessageType messageType) { - size_t size = dlc; - uint32_t tps = (size + SIZE_PACKET-1) / SIZE_PACKET; // Calculate the number of packets needed + messageID = generateMessageID(messageType, srcID); + tps = (size + SIZE_PACKET-1) / SIZE_PACKET; // Calculate the number of packets needed + bool isBroadcast = (messageType == MessageType::ERROR_MESSAGE); + bool isRTR = (messageType == MessageType::REMOTE_TRANSMISSION_REQUEST); for (uint32_t i = 0; i < tps; ++i) { uint8_t packetData[SIZE_PACKET]; size_t copySize = std::min(size - i * SIZE_PACKET, (size_t)SIZE_PACKET); // Determine how much data to copy for each packet - std::memcpy(packetData, (uint8_t *)data + i * SIZE_PACKET, copySize); - uint32_t id = srcID + destID; - packets.emplace_back(id, i, tps, srcID, destID, packetData, copySize, isBroadcast, false, false); + std::memcpy(packetData, (uint8_t*)data + i * SIZE_PACKET, copySize); + packets.emplace_back(messageID, i, tps, srcID, destID, copySize, isRTR, isBroadcast, packetData); } } +uint32_t Message::generateMessageID(MessageType messageType, uint16_t srcID) { + uint32_t messageTypeID = static_cast(messageType); + uint32_t srcID32 = static_cast(srcID); + + auto now = std::chrono::system_clock::now().time_since_epoch(); + uint32_t timestamp = static_cast( + std::chrono::duration_cast(now).count() & 0xFF + ); + + return (messageTypeID << 24) | + (srcID32 << 8) | + timestamp; +} + // Constructor for receiving message Message::Message(uint32_t tps) { @@ -23,13 +38,11 @@ Message::Message(uint32_t tps) // Add a packet to the received message bool Message::addPacket(const Packet &p) { - // Implementation according to the CAN BUS - - // For further testing - // if (p.header.PSN >= packets.size()) { - // return false; - // } - + if (!packets.empty()) { + if (p.getPSN() <= packets.back().getPSN()) { + return false; + } + } packets.push_back(p); return true; } @@ -43,10 +56,10 @@ bool Message::isComplete() const // Get the complete data of the message void *Message::completeData() const { - size_t totalSize = (tps - 1) * SIZE_PACKET + packets.back().header.DLC; + size_t totalSize = (tps - 1) * SIZE_PACKET + packets.back().getDLC(); void *data = malloc(totalSize); for (const auto &packet : packets) { - std::memcpy(static_cast(data) + packet.header.PSN * SIZE_PACKET, packet.data, packet.header.DLC); + std::memcpy((uint8_t *)data + packet.getPSN() * SIZE_PACKET, packet.getPayload(), packet.getDLC()); } return data; } @@ -56,3 +69,24 @@ std::vector &Message::getPackets() { return packets; } + +// Extract the MessageType from the message ID +MessageType Message::getMessageTypeFromID(uint32_t messageID) +{ + uint32_t messageTypeID = (messageID >> 24) & 0xFF; // Extract the message type + return static_cast(messageTypeID); // Cast to MessageType +} + +// Returns a string representation of the MessageType enum value +std::string Message::getMessageTypeString(MessageType type) +{ + switch (type) { + case MessageType::INITIAL_CONNECTION: return "INITIAL_CONNECTION"; + case MessageType::ERROR_MESSAGE: return "ERRORE"; + case MessageType::ACK: return "ACK"; + case MessageType::OVERLOAD_MESSAGE: return "OVERLOAD"; + case MessageType::REMOTE_TRANSMISSION_REQUEST: return "RTRT"; + case MessageType::DATA_MESSAGE: return "DATA"; + default: return "UNKNOWN"; + } +} \ No newline at end of file diff --git a/communication/src/packet.cpp b/communication/src/packet.cpp index 507755ed..7f4aa372 100644 --- a/communication/src/packet.cpp +++ b/communication/src/packet.cpp @@ -1,45 +1,119 @@ #include "../include/packet.h" + // Constructor to initialize Packet for sending -Packet::Packet(uint32_t id, uint32_t psn, uint32_t tps, uint32_t srcID, uint32_t destID, void *data, uint8_t dlc, bool isBroadcast, bool RTR, bool passive) +Packet::Packet(uint32_t id, uint32_t PSN, uint32_t TPS, uint32_t srcId, + uint32_t destId, int DLC, bool RTR, bool isBroadcast, + const uint8_t* payload) { - header.ID = id; - header.PSN = psn; - header.TPS = tps; - header.SrcID = srcID; - header.DestID = destID; - header.DLC = dlc; - std::memcpy(this->data, data, dlc); - header.CRC = calculateCRC(data, dlc); - header.timestamp = std::time(nullptr); + header.id = id; + header.PSN = PSN; + header.TPS = TPS; + header.srcId = srcId; + header.destId = destId; + header.DLC = DLC; header.RTR = RTR; - header.passive = passive; + header.passive = false; // Default value + header.timestamp = 0; // Default value header.isBroadcast = isBroadcast; + + // Ensure DLC does not exceed payload size + size_t copySize = std::min(static_cast(DLC), sizeof(this->payload)); + + // Copy payload into the payload array if DLC > 0 + if (DLC > 0 && payload) + { + std::memcpy(this->payload, payload, copySize); + } + else + { + std::memset(this->payload, 0, sizeof(this->payload)); // Zero out payload if no data + } + + // Calculate and set CRC after setting other header fields + header.CRC = calculateCRC(); } // Constructor to initialize receiving Packet ID for init Packet::Packet(uint32_t id) { std::memset(&header, 0, sizeof(header)); // Initialize all fields to zero - header.SrcID = id; - header.timestamp = std::time(nullptr); + header.srcId = id; } -// Implementation according to the CAN BUS -uint16_t Packet::calculateCRC(const void *data, size_t length) +// Overloaded operator> +bool Packet::operator>(const Packet &other) const { - uint16_t crc = 0xFFFF; - // Calculate CRC for the given data and length - return crc; + return header.id < other.header.id; +} + +// CRC calculation +uint16_t Packet::calculateCRC() const +{ + const uint16_t polynomial = 0x4599; // Polynomial for CRC-15 CAN + uint16_t crc = 0x0000; // Initial CRC value for CAN Bus + + // Combine header fields for CRC calculation (without the CRC field itself) + crc ^= header.id; + crc = (crc << 1) ^ polynomial; + + crc ^= header.PSN; + crc = (crc << 1) ^ polynomial; + + crc ^= header.TPS; + crc = (crc << 1) ^ polynomial; + + crc ^= header.srcId; + crc = (crc << 1) ^ polynomial; + + crc ^= header.destId; + crc = (crc << 1) ^ polynomial; + + crc ^= header.DLC; + crc = (crc << 1) ^ polynomial; + + crc ^= header.RTR; + crc = (crc << 1) ^ polynomial; + + crc ^= header.isBroadcast; + crc = (crc << 1) ^ polynomial; + + // Process payload bytes for CRC calculation + for (int i = 0; i < header.DLC; ++i) + { + crc ^= payload[i] << 8; + for (int j = 0; j < 8; ++j) + { + if (crc & 0x8000) + { + crc = (crc << 1) ^ polynomial; + } + else + { + crc <<= 1; + } + } + } + + // Mask CRC to 15 bits as per the CAN protocol + return crc & 0x7FFF; +} + +// Validate CRC +bool Packet::validateCRC() const +{ + return calculateCRC() == header.CRC; } // A function to convert the data to hexa (logger) -std::string Packet::pointerToHex(const void* data, size_t size) const +std::string Packet::pointerToHex() const { - const unsigned char* byteData = reinterpret_cast(data); - std::ostringstream oss; - oss<<"0x"; - for (size_t i = 0; i < size; ++i) - oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(byteData[i]); + std::ostringstream oss; // Stream for constructing the hexadecimal output + const uint8_t* data = getPayload(); // Get the payload data + int dlc = getDLC(); // Get the data length code (DLC) + + for (int i = 0; i < dlc; ++i) { + oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(data[i]); + } - return oss.str(); + return oss.str(); // Return the hex string representation of the payload } \ No newline at end of file diff --git a/communication/src/server_connection.cpp b/communication/src/server_connection.cpp index c8453e73..1dc57bcd 100644 --- a/communication/src/server_connection.cpp +++ b/communication/src/server_connection.cpp @@ -101,7 +101,7 @@ void ServerConnection::handleClient(int clientSocket) if (valread <= 0) return; - uint32_t clientID = packet.header.SrcID; + uint32_t clientID = packet.getSrcId(); if(!isValidId(clientID)) return; @@ -161,7 +161,7 @@ int ServerConnection::getClientSocketByID(uint32_t destID) // Sends the message to destination ErrorCode ServerConnection::sendDestination(const Packet &packet) { - int targetSocket = getClientSocketByID(packet.header.DestID); + int targetSocket = getClientSocketByID(packet.getDestId()); if (targetSocket == -1) return ErrorCode::INVALID_CLIENT_ID; From 1821abd64e8fee7c04cf7dee3ba54786c71a9256 Mon Sep 17 00:00:00 2001 From: Shoshi Date: Tue, 1 Oct 2024 12:22:25 +0300 Subject: [PATCH 14/18] Implement CAN BUS collision handling simulation --- communication/include/bus_manager.h | 68 ++++++++----- communication/include/client_connection.h | 1 + communication/include/communication.h | 1 + communication/src/bus_manager.cpp | 118 ++++++++++++++++------ communication/src/client_connection.cpp | 2 +- communication/src/communication.cpp | 2 +- 6 files changed, 131 insertions(+), 61 deletions(-) diff --git a/communication/include/bus_manager.h b/communication/include/bus_manager.h index fa00bc0c..a0e19194 100644 --- a/communication/include/bus_manager.h +++ b/communication/include/bus_manager.h @@ -1,43 +1,55 @@ #pragma once #include -#include -#include "server_connection.h" +#include +#include +#include "../include/server_connection.h" #include +#include "packet.h" +#include "global_clock.h" -class BusManager -{ -private: - ServerConnection server; - - // Singleton instance - static BusManager* instance; - static std::mutex managerMutex; - //SyncCommunication syncCommunication; - - // Sending according to broadcast variable - ErrorCode sendToClients(const Packet &packet); - - // Private constructor - BusManager(std::vector idShouldConnect, uint32_t limit); +// Manager class responsible for handling CAN BUS-like communication and collision management +class BusManager { + public: + // Returns the singleton instance of Manager + static BusManager *getInstance(std::vector idShouldConnect, + uint32_t limit); -public: - //Static function to return a singleton instance - static BusManager* getInstance(std::vector idShouldConnect, uint32_t limit); - - // Sends to the server to listen for requests + // Starts server connection ErrorCode startConnection(); // Stops server connection static void stopConnection(); - // Receives the packet that arrived and checks it before sending it out + // Receives a packet and checks for collisions before sending void receiveData(Packet &p); - // Implementation according to the conflict management of the CAN bus protocol - Packet checkCollision(Packet ¤tPacket); + // Destructor for cleaning up + ~BusManager(); + private: + ServerConnection server; // Handles communication with the server + static BusManager *instance; // Singleton instance + static std::mutex managerMutex; // Mutex for singleton + Packet *lastPacket; // Stores the last packet received + std::mutex lastPacketMutex; // Protects access to lastPacket + std::atomic stopFlag; // Indicates if the collision timer should stop + std::thread collisionTimerThread; // Thread for collision management + + // Private constructor to ensure singleton pattern + BusManager(std::vector idShouldConnect, uint32_t limit); + + // Sends packet to clients based on whether it's broadcast or unicast + ErrorCode sendToClients(const Packet &packet); + + // Starts the timer to check for packet collisions + void startCollisionTimer(); + + // Checks if the current packet collides with the last one + void checkCollision(Packet ¤tPacket); + + // Determines packet priority in case of collision, based on CAN BUS protocol + Packet *packetPriority(Packet &a, Packet &b); - // Implement a priority check according to the CAN bus - Packet packetPriority(Packet &a, Packet &b); + // Sends the last packet if necessary and clears it + void checkLastPacket(); - ~BusManager(); }; \ No newline at end of file diff --git a/communication/include/client_connection.h b/communication/include/client_connection.h index 3e31c863..8a95eaea 100644 --- a/communication/include/client_connection.h +++ b/communication/include/client_connection.h @@ -12,6 +12,7 @@ #include "../sockets/real_socket.h" #include #include "error_code.h" +#include "../include/global_clock.h" #define PORT 8080 #define IP "127.0.0.1" diff --git a/communication/include/communication.h b/communication/include/communication.h index 79ce295a..5a5b3bef 100644 --- a/communication/include/communication.h +++ b/communication/include/communication.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include "client_connection.h" #include "../sockets/Isocket.h" #include "error_code.h" diff --git a/communication/src/bus_manager.cpp b/communication/src/bus_manager.cpp index 57fbd797..79ebaad9 100644 --- a/communication/src/bus_manager.cpp +++ b/communication/src/bus_manager.cpp @@ -1,18 +1,23 @@ #include "../include/bus_manager.h" -BusManager* BusManager::instance = nullptr; +// Initialize static instance to nullptr +BusManager *BusManager::instance = nullptr; std::mutex BusManager::managerMutex; -//Private constructor -BusManager::BusManager(std::vector idShouldConnect, uint32_t limit) :server(8080, std::bind(&BusManager::receiveData, this, std::placeholders::_1))//,syncCommunication(idShouldConnect, limit) +// Private constructor: initializes the server and starts the collision timer +BusManager::BusManager(std::vector idShouldConnect, uint32_t limit) + : server(8080, + std::bind(&BusManager::receiveData, this, std::placeholders::_1)), + lastPacket(nullptr), stopFlag(false) { - // Setup the signal handler for SIGINT + startCollisionTimer(); // Start collision management timer } -// Static function to return a singleton instance -BusManager* BusManager::getInstance(std::vector idShouldConnect, uint32_t limit) { +// Singleton getter: ensures only one instance of Manager +BusManager *BusManager::getInstance(std::vector idShouldConnect, + uint32_t limit) +{ if (instance == nullptr) { - // Lock the mutex to prevent multiple threads from creating instances simultaneously std::lock_guard lock(managerMutex); if (instance == nullptr) { instance = new BusManager(idShouldConnect, limit); @@ -21,44 +26,88 @@ BusManager* BusManager::getInstance(std::vector idShouldConnect, uint3 return instance; } -// Sends to the server to listen for requests +// Starts the server connection and listens for incoming requests ErrorCode BusManager::startConnection() { - - ErrorCode isConnected = server.startConnection(); - //syncCommunication.notifyProcess() - return isConnected; + return server.startConnection(); } -// Receives the packet that arrived and checks it before sending it out +// Receives a packet, checks for collisions, and sends it if valid void BusManager::receiveData(Packet &p) { - ErrorCode res = sendToClients(p); - - // Checking the case of collision and priority in functions : checkCollision,packetPriority - // Packet* resolvedPacket = checkCollision(*p); - // if (resolvedPacket) - // server.sendToClients(*resolvedPacket); + checkCollision(p); // Handle packet collisions } -// Sending according to broadcast variable +// Sends a packet either as broadcast or to a specific destination ErrorCode BusManager::sendToClients(const Packet &packet) { - if(packet.getIsBroadcast()) - return server.sendBroadcast(packet); - return server.sendDestination(packet); + if (packet.getIsBroadcast()) { + return server.sendBroadcast(packet); // Broadcast message + } + return server.sendDestination(packet); // Send to specific client } -// Implement according to the conflict management of the CAN bus protocol -Packet BusManager::checkCollision(Packet ¤tPacket) +// Manages collisions based on the CAN BUS protocol +void BusManager::checkCollision(Packet ¤tPacket) { - return currentPacket; + std::lock_guard lock(lastPacketMutex); + if (lastPacket == nullptr) { + // No previous packet, store the current one + lastPacket = new Packet(currentPacket); + if (lastPacket == nullptr) { + } + } + else { + if (lastPacket->getTimestamp() == currentPacket.getTimestamp()) { + // Same timestamp indicates potential collision, check priority + Packet *prioritizedPacket = + packetPriority(*lastPacket, currentPacket); + if (prioritizedPacket == ¤tPacket) { + delete lastPacket; // Replace last packet if current packet has priority + lastPacket = new Packet(currentPacket); + } + } + } } -// Implement a priority check according to the CAN bus -Packet BusManager::packetPriority(Packet &a, Packet &b) +// Determines which packet has higher priority (CAN BUS logic) +Packet *BusManager::packetPriority(Packet &a, Packet &b) { - return (a.getSrcId() < b.getSrcId()) ? a : b; + if (a.getIsPassive() && !b.getIsPassive()) { + return &b; // Non-passive packet takes priority + } + else if (!a.getIsPassive() && b.getIsPassive()) { + return &a; // Non-passive packet takes priority + } + else { + return (a > b) ? &a + : &b; // If both are the same type, compare based on ID + } +} + +// Starts the collision timer to periodically check for packet collisions +void BusManager::startCollisionTimer() +{ + stopFlag = false; + collisionTimerThread = std::thread([this]() { + while (!stopFlag) { + GlobalClock::waitForNextTick(); + if (!stopFlag) { + checkLastPacket(); // Check and send last packet if necessary + } + } + }); +} + +// Checks the last packet and sends it if it hasn't been sent yet +void BusManager::checkLastPacket() +{ + std::lock_guard lock(lastPacketMutex); + if (lastPacket != nullptr) { + ErrorCode res = sendToClients(*lastPacket); + delete lastPacket; // Clear last packet after sending + lastPacket = nullptr; + } } //shut down the server @@ -69,6 +118,13 @@ void BusManager::stopConnection() } } -BusManager::~BusManager() { +// Destructor: ensures proper cleanup of threads and resources +BusManager::~BusManager() +{ + stopFlag = true; // Stop collision timer thread + if (collisionTimerThread.joinable()) { + collisionTimerThread.join(); + } + delete lastPacket; // Clean up lastPacket instance = nullptr; -} +} \ No newline at end of file diff --git a/communication/src/client_connection.cpp b/communication/src/client_connection.cpp index d3035c72..6990f7ad 100644 --- a/communication/src/client_connection.cpp +++ b/communication/src/client_connection.cpp @@ -44,7 +44,7 @@ ErrorCode ClientConnection::sendPacket(Packet &packet) //If send executed before start if (!connected) return ErrorCode::CONNECTION_FAILED; - + packet.setTimestamp(GlobalClock::getCurrentTick()); ssize_t bytesSent = socketInterface->send(clientSocket, &packet, sizeof(Packet), 0); if (bytesSent==0) { closeConnection(); diff --git a/communication/src/communication.cpp b/communication/src/communication.cpp index 3d716aaa..5521f7d6 100644 --- a/communication/src/communication.cpp +++ b/communication/src/communication.cpp @@ -1,5 +1,4 @@ #include "../include/communication.h" -#include Communication* Communication::instance = nullptr; @@ -50,6 +49,7 @@ ErrorCode Communication::sendMessage(void *data, size_t dataSize, uint32_t destI ErrorCode res = client.sendPacket(packet); if (res != ErrorCode::SUCCESS) return res; + std::this_thread::sleep_for(std::chrono::seconds(2)); } return ErrorCode::SUCCESS; From 1c3ff3e16772d8f0990934aefc9e1c21f7ecde4b Mon Sep 17 00:00:00 2001 From: tzivya-zalaznik Date: Tue, 1 Oct 2024 13:30:38 +0300 Subject: [PATCH 15/18] Add Scheduler class to manage retransmission timers and track ACKs --- communication/include/scheduler.h | 81 ++++++++++++++++++++++++++++++ communication/src/scheduler.cpp | 83 +++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 communication/include/scheduler.h create mode 100644 communication/src/scheduler.cpp diff --git a/communication/include/scheduler.h b/communication/include/scheduler.h new file mode 100644 index 00000000..9167c4ef --- /dev/null +++ b/communication/include/scheduler.h @@ -0,0 +1,81 @@ +#ifndef __SCHEDULER_H__ +#define __SCHEDULER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include "global_clock.h" + +// must be set +#define MAX_ACK_TIMEOUT \ + (20 * TICK_DURATION) ///< Maximum time to wait for ACK + +/** + * @class Scheduler + * @brief Manages retransmission timers and tracks acknowledgments for packets. + * + * This class provides functionality to manage retransmission timers for + * packets, handle acknowledgments, and clean up packet data once + * retransmissions are complete. + */ +class Scheduler +{ +public: + using Callback = std::function; + + Scheduler(); + ~Scheduler(); + + /** + * @brief Stops all active timers and waits for their completion. + * + * This method ensures that all active timers (threads) complete their + * execution before the program exits. + */ + void stopAllTimers(); + + /** + * @brief Starts a retransmission timer for a given packet ID. + * + * The function initiates a timer to wait for an ACK (Acknowledgment) for the specified packet. + * If the ACK is received within the MAX_ACK_TIMEOUT, it clears the packet data and sets the result + * of the ackPromise to `true`, indicating success. If the timeout occurs and no ACK is received, + * it triggers the provided callback function to retransmit the packet and increments the retry count. + * + * @param packetID The unique ID of the packet being transmitted. + * @param callback The callback function to call when retransmitting the packet after a timeout. + * @param ackPromise A shared promise that communicates whether the packet transmission was successful + * (i.e., ACK was received). + */ + void startRetransmissionTimer(int packetID, Callback callback, std::shared_ptr> ackPromise); + + /** + * @brief Receives an acknowledgment for a packet. + * + * @param packetID The ID of the packet that has been acknowledged. + */ + void receiveACK(int packetID); + + /** + * @brief Clears the data associated with a packet. + * + * @param packetID The ID of the packet whose data is to be cleared. + */ + void clearPacketData(int packetID); + +private: + std::unordered_map + ackReceived; ///< Map to track received acknowledgments + std::unordered_map + retryCounts; ///< Map to track retry counts for packets + std::mutex mutex; ///< Mutex for thread safety + std::condition_variable cv; ///< Condition variable for synchronization + std::vector> + futures; ///< Vector to store futures of active threads +}; + +#endif // __SCHEDULER_H__ diff --git a/communication/src/scheduler.cpp b/communication/src/scheduler.cpp new file mode 100644 index 00000000..f070d950 --- /dev/null +++ b/communication/src/scheduler.cpp @@ -0,0 +1,83 @@ +#include "../include/scheduler.h" + +Scheduler::Scheduler() +{ +} + +Scheduler::~Scheduler() +{ + stopAllTimers(); +} + +void Scheduler::stopAllTimers() +{ + for (auto &future : futures) + { + if (future.valid()) + { + future.wait(); // Wait for all threads to finish + } + } +} + +void Scheduler::startRetransmissionTimer(int packetID, Callback callback, std::shared_ptr> ackPromise) +{ + // Promise to manage the lifecycle of the thread itself + std::promise threadCompletionPromise; + + // Future object to track the thread's completion + std::future future = threadCompletionPromise.get_future(); + + // Store the future to ensure we can wait for the thread to finish later + futures.push_back(std::move(future)); + + // Start a new thread for handling retransmission and ACK wait + std::thread([this, packetID, callback, threadCompletionPromise = std::move(threadCompletionPromise), ackPromise]() mutable + { + int retryCount = 0; + + { + // Lock the mutex to synchronize access to shared data (ackReceived) + std::unique_lock lock(mutex); + + // Wait for an ACK or timeout + if (cv.wait_for(lock, MAX_ACK_TIMEOUT, [this, packetID]() + { return ackReceived[packetID]; })) + { + // ACK received within the timeout period + clearPacketData(packetID); // Clear packet data + + // Set both promises to indicate success and thread completion + threadCompletionPromise.set_value(); + ackPromise->set_value(true); // ACK was received, set to true + return; // Exit the thread + } + else + { + // Timeout occurred, retransmit the packet + retryCounts[packetID]++; + retryCount = retryCounts[packetID]; + } + } + + // Call the callback function with the updated retry count + callback(retryCount); + + // Set the promise to indicate the thread has finished + threadCompletionPromise.set_value(); + }) + .detach(); // Detach the thread to allow it to run independently +} + +void Scheduler::receiveACK(int packetID) +{ + std::unique_lock lock(mutex); + ackReceived[packetID] = true; + cv.notify_all(); // Notify all waiting threads +} + +void Scheduler::clearPacketData(int packetID) +{ + ackReceived.erase(packetID); + retryCounts.erase(packetID); +} \ No newline at end of file From cf67fc5eafd26961ec6e4af774156af73c107ba6 Mon Sep 17 00:00:00 2001 From: tzivya-zalaznik Date: Tue, 1 Oct 2024 14:53:13 +0300 Subject: [PATCH 16/18] RTO Mechanism for Retransmission - Acknowledgment reception and handling - Error handling - Communication component states - Bus Off recovery mechanism --- communication/include/communication.h | 384 ++++++++++++++++++++++--- communication/src/communication.cpp | 393 +++++++++++++++++++++----- 2 files changed, 659 insertions(+), 118 deletions(-) diff --git a/communication/include/communication.h b/communication/include/communication.h index 5a5b3bef..34736d95 100644 --- a/communication/include/communication.h +++ b/communication/include/communication.h @@ -1,67 +1,359 @@ -#pragma once +#ifndef __COMMUNICATION_H__ +#define __COMMUNICATION_H__ + #include +#include +#include +#include #include +#include #include "client_connection.h" -#include "../sockets/Isocket.h" #include "error_code.h" -class Communication -{ -private: - ClientConnection client; - std::unordered_map receivedMessages; - void (*passData)(uint32_t, void *); - uint32_t id; - //SyncCommunication syncCommunication; - - // A static variable that holds an instance of the class - static Communication* instance; - - // Accepts the packet from the client and checks.. - void receivePacket(Packet &p); - - // Checks if the packet is intended for him - bool checkDestId(Packet &p); - - // Checks if the Packet is currect - bool validCRC(Packet &p); - - // Receives the packet and adds it to the message - void handlePacket(Packet &p); - - // Implement error handling according to CAN bus - void handleError(); - - // Implement arrival confirmation according to the CAN bus - Packet hadArrived(); - - // Adding the packet to the complete message - void addPacketToMessage(Packet &p); - - // Static method to handle SIGINT signal +#include "scheduler.h" +#include "message.h" +#include "packet.h" + +#define THRESHOLD_TO_BUSOFF 256 ///< Threshold for entering BusOff state +#define THRESHOLD_TO_PASSIVE 128 ///< Threshold for entering Passive state +#define BUSOFF_RECOVERY_TIME (128 * TICK_DURATION) ///< Time required to recover from BusOff state +#define MAX_RETRANSMISSIONS 10 ///< Maximum allowed retransmissions for a packet + +// Enumeration for different error states of the communication +enum class ErrorState { + Active, ///< The communication system is operating normally. + Passive, ///< The communication system has issues and is losing priority during collisions. + BusOff ///< The communication system is in BusOff state and cannot send or receive messages. +}; + +// Communication class to handle message transmission and reception +class Communication { + private: + ClientConnection client; ///< ClientConnection object for managing the connection + std::unordered_map receivedMessages; ///< Map to store received messages + void (*passData)(uint32_t, void *); ///< Callback function for passing data to the client + uint32_t id; ///< Unique identifier for the communication instance + static Communication *instance; ///< Static variable that holds an instance of the class + + // Error tracking variables + std::mutex mtx; ///< Mutex to protect access to error counts and state + int TEC; ///< Transmission Error Count + int REC; ///< Receive Error Count + ErrorState state; ///< Current system state + + static std::thread busOffRecoveryThread; ///< Thread for handling BusOff recovery + + // RTO (Retransmission Timeout) handling + Scheduler scheduler; ///< Scheduler for managing transmission timing + + // Packet reception and validation methods + + /** + * @brief Receives a packet and processes it based on its type. + * + * This method checks if the bus is in the BusOff state and exits if so. + * It then verifies the destination ID of the packet. + * If valid, it performs a CRC check for data integrity. + * If the CRC check fails, an error message is sent to the source. + * + * Upon successful validation, the method determines the packet type: + * - For data messages, it calls `handleDataPacket`. + * - For error messages, it calls `handleErrorPacket`. + * - For acknowledgment messages, it calls `handleAckPacket`. + * + * @param packet The packet object to be received and processed. + */ + void receivePacket(Packet &packet); + + /** + * @brief Checks the destination ID of the packet. + * + * This method determines whether the packet is intended for this communication node. + * It returns true if the packet is either a broadcast or if its destination ID matches + * the ID of the current communication node. + * + * @param packet The packet object containing destination information. + * @return Returns true if the packet is a broadcast or if the destination ID matches the node's ID. + */ + bool checkDestId(Packet &packet); + + /** + * @brief Validates the CRC of the packet. + * + * This method checks the integrity of the packet by validating its CRC (Cyclic Redundancy Check). + * It calls the packet's own method to perform the CRC validation. + * + * @param packet The packet object whose CRC is to be validated. + * @return Returns true if the CRC is valid, false otherwise. + */ + bool validCRC(Packet &packet); + + /** + * @brief Handles incoming data packets. + * + * This method processes data packets by adding them to the message queue. + * After processing the packet, it sends an acknowledgment (ACK) back to the source. + * + * @param packet The data packet to be handled. + */ + void handleDataPacket(Packet &packet); + + /** + * @brief Handles incoming error packets. + * + * This method processes error packets based on their destination ID. + * If the packet is intended for this node, it handles the transmission error. + * Otherwise, it handles the reception error. + * + * @param packet The error packet to be handled. + */ + void handleErrorPacket(Packet &packet); + + /** + * @brief Handles acknowledgment packets. + * + * This method processes acknowledgment packets by retrieving the + * received message ID from the packet's payload. It then notifies + * the scheduler of the received acknowledgment and handles the + * successful transmission. + * + * @param packet The acknowledgment packet to be handled. + */ + void handleAckPacket(Packet &packet); + + /** + * @brief Adds a received packet to the corresponding message. + * + * This method checks if a message with the given ID already exists + * in the received messages. If it does, the packet is added to that + * message. If not, a new message is created. When the message is + * complete, the complete data is passed to the next stage of + * processing, and the message is removed from the received messages. + * + * @param packet The packet to be added to the message. + */ + void addPacketToMessage(Packet &packet); + + // BusOff recovery methods + + /** + * @brief Initiates the recovery process from the BusOff state. + * + * This method checks if the communication system is in the BusOff state + * and if the bus off recovery thread is not already running. If both conditions + * are met, it starts a new thread to handle the recovery process via the + * `busOffTimer` method. + */ + void recoverFromBusOff(); + + /** + * @brief Handles the timer for recovering from the BusOff state. + * + * This method puts the thread to sleep for a defined recovery time + * (BUSOFF_RECOVERY_TIME) and then resets the state of the communication system. + */ + void busOffTimer(); + + /** + * @brief Cleans up the BusOff recovery thread. + * + * This method checks if the BusOff recovery thread is joinable. + * If it is, the thread is joined to ensure proper cleanup and resource management. + */ + static void cleanupBusOffRecoveryThread(); + + // State checking and error handling methods + + /** + * @brief Checks and updates the communication state based on error counters. + * + * This method evaluates the Transmission Error Counter (TEC) and Receive Error Counter (REC) to determine + * the current state of the communication system. If TEC exceeds the threshold for BusOff, the state is + * set to BusOff. If TEC or REC exceeds the threshold for Passive, the state is set to Passive. + * Otherwise, the state is set to Active. If the state is BusOff, the recovery process is initiated. + */ + void checkState(); + + /** + * @brief Handles the transmission error by updating the Transmission Error Counter (TEC). + * + * This method increments the Transmission Error Counter (TEC) by a defined value (8) + * to reflect a transmission error. It then calls `checkState` to reassess the + * communication state based on the updated error counter. + */ + void handleTransmitError(); + + /** + * @brief Handles acknowledgment errors by updating the Transmission Error Counter (TEC). + * + * This method increments the Transmission Error Counter (TEC) by 1 to reflect + * an acknowledgment error. It then calls `checkState` to reassess the communication + * state based on the updated error counter. + * + * @param packet The packet object associated with the acknowledgment error. + */ + void handleACKError(Packet &packet); + + /** + * @brief Handles reception errors by updating the Reception Error Counter (REC). + * + * This method increments the Reception Error Counter (REC) by 1 to reflect + * a reception error. It then calls `checkState` to reassess the communication + * state based on the updated error counter. + */ + void handleReceiveError(); + + /** + * @brief Handles successful transmissions by updating the Transmit Error Counter (TEC). + * + * This method decrements the Transmit Error Counter (TEC) by 1 to reflect + * a successful transmission, as long as the counter is greater than zero. + * It then calls `checkState` to reassess the communication state based on + * the updated error counter. + */ + void handleSuccessfulTransmission(); + + /** + * @brief Resets the communication state by clearing error counters and setting state to active. + * + * This method resets the Transmit Error Counter (TEC) and Receive Error Counter (REC) + * to zero, and sets the communication state to `Active`. This function ensures that the + * communication system starts fresh after experiencing issues, allowing normal operation + * to resume. + */ + void resetState(); + + /** + * @brief Sends a packet with a retransmission timeout (RTO). + * + * This method attempts to send a packet, checking the system state and the + * number of retransmissions. If the system is in the BusOff state or the + * maximum retransmissions have been exceeded, it stops sending the packet. + * + * passive state based on the current system state. It waits for an acknowledgment + * (ACK) and handles retransmissions if needed. + * + * @param packet The packet object to be sent. + * @param retransmissions The number of retransmissions attempted (default is 0). + * @return True if the packet was sent successfully, false otherwise. + */ + bool sendPacketWithRTO(Packet &packet, int retransmissions = 0); + + /** + * @brief Sends a packet to the designated recipient. + * + * This method checks if the system is in the BusOff state. If it is, the function + * immediately returns false. + * and sets the packet's passive state based on the current system state. + * + * The method attempts to send the packet using the client and returns true if + * the transmission is successful. + * + * @param packet The packet object to be sent. + * @return True if the packet was sent successfully, false otherwise. + */ + bool sendPacket(Packet &packet); + + /** + * @brief Handles signals for the communication instance. + * + * This function is called when a signal is received. It performs cleanup operations, + * including closing the client connection and waiting for active message threads to complete. + * It exits the program with the provided signal number. + * + * @param signum The signal number that triggered this handler. + */ static void signalHandler(int signum); + /** + * @brief Sets the ID for the communication instance. + * + * This function updates the ID used for communication purposes. + * + * @param newId The new ID to be set for the instance. + */ void setId(uint32_t newId); + /** + * @brief Sets the callback function for passing data. + * + * This function allows setting a custom callback function that will be called to pass data. + * + * @param callback A pointer to the function that will handle data passing. + */ void setPassDataCallback(void (*callback)(uint32_t, void *)); -public: - // Constructor + public: + //Cconstructor and destructor + /** + * @brief Constructs a Communication object. + * + * This constructor initializes the communication instance with the given ID and + * sets the callback for passing data. It also sets a signal handler for SIGINT + * and initializes the error counters and state. + * + * @param id The unique identifier for the communication instance. + * @param passDataCallback The callback function to pass data. + */ Communication(uint32_t id, void (*passDataCallback)(uint32_t, void *)); - - // Sends the client to connect to server + /** + * @brief Destroys the Communication object. + * + * This destructor performs cleanup operations, including waiting for active + * message threads and cleaning up any recovery threads. + */ + ~Communication(); + + //connection management + + /** + * @brief Starts the connection to the server. + * + * This function attempts to establish a connection with the server using the + * communication instance's ID. + * + * @return ErrorCode indicating the result of the operation. + */ ErrorCode startConnection(); - - // Sends a message to manager + + //Sending messages + + /** + * @brief Sends a message synchronously. + * + * This function sends a message to a specified destination ID with the provided + * data and size. It validates input parameters before sending and returns an + * error code based on the result of the sending operation. + * + * @param data Pointer to the data to send. + * @param dataSize Size of the data in bytes. + * @param destID The destination ID for the message. + * @param srcID The source ID of the message. + * @param messageType The type of message being sent (default is DATA_MESSAGE). + * @return ErrorCode indicating the result of the operation. + */ ErrorCode sendMessage(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, MessageType messageType = MessageType::DATA_MESSAGE); - + // Sends a message to manager - Async void sendMessageAsync(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, std::function sendCallback, MessageType messageType = MessageType::DATA_MESSAGE); - //Destructor - ~Communication(); -}; \ No newline at end of file + /** + * @brief Retrieves the current state of the communication system. + * + * This method returns the current communication state, which may be one of the following: + * - Active + * - Passive + * - BusOff + * + * The function locks the mutex to ensure thread safety while accessing the state. + * + * @return ErrorState The current communication state. + */ + ErrorState getState(); +}; + +#endif // __COMMUNICATION_H__ diff --git a/communication/src/communication.cpp b/communication/src/communication.cpp index 5521f7d6..60ae04d7 100644 --- a/communication/src/communication.cpp +++ b/communication/src/communication.cpp @@ -1,21 +1,34 @@ #include "../include/communication.h" -Communication* Communication::instance = nullptr; +// Initialize static member variables for the Communication class +Communication *Communication::instance = nullptr; +std::thread Communication::busOffRecoveryThread; // Constructor -Communication::Communication(uint32_t id, void (*passDataCallback)(uint32_t, void *)) : - client(std::bind(&Communication::receivePacket, this, std::placeholders::_1)) +Communication::Communication(uint32_t id, void (*passDataCallback)(uint32_t, void *)) + : TEC(0), REC(0), state(ErrorState::Active), + client(std::bind(&Communication::receivePacket, this, + std::placeholders::_1)) { - setId(id); - setPassDataCallback(passDataCallback); + setId(id); // Set the communication ID + setPassDataCallback(passDataCallback); // Set the callback for data passing - instance = this; + instance = this; // Initialize the singleton instance + // Set signal handler for SIGINT auto signalResult = signal(SIGINT, Communication::signalHandler); if (signalResult == SIG_ERR) throw std::runtime_error("Failed to set signal handler for SIGINT"); } +// Destructor +Communication::~Communication() +{ + cleanupBusOffRecoveryThread(); // Clean up the bus off recovery thread + instance->client.closeConnection(); // Close the client connection + instance = nullptr; // Clear the singleton instance +} + // Sends the client to connect to server ErrorCode Communication::startConnection() { @@ -27,32 +40,36 @@ ErrorCode Communication::startConnection() return isConnected; } -// Sends a message sync -ErrorCode Communication::sendMessage(void *data, size_t dataSize, uint32_t destID, - uint32_t srcID, - MessageType messageType) +// Send message synchronously +ErrorCode Communication::sendMessage( + void *data, size_t dataSize, uint32_t destID, uint32_t srcID, + MessageType messageType /*= MessageType::DATA_MESSAGE*/) { if (dataSize == 0) - return ErrorCode::INVALID_DATA_SIZE; - + return ErrorCode::INVALID_DATA_SIZE; // Check for valid data size if (data == nullptr) - return ErrorCode::INVALID_DATA; - + return ErrorCode::INVALID_DATA; // Check for valid data pointer + if (destID == 0 || srcID == 0) + return ErrorCode::INVALID_ID; // Check for valid IDs if (!client.isConnected()) - return ErrorCode::CONNECTION_FAILED; + return ErrorCode::CONNECTION_FAILED; // Check for connection status + + Message msg(srcID, destID, data, dataSize, + messageType); // Create a new message - Message msg(srcID, destID, data, dataSize,messageType); // Create a new message //Sending the message to logger RealSocket::log.logMessage(logger::LogLevel::INFO,std::to_string(srcID),std::to_string(destID),"Complete message:" + msg.getPackets().at(0).pointerToHex()); for (auto &packet : msg.getPackets()) { - ErrorCode res = client.sendPacket(packet); - if (res != ErrorCode::SUCCESS) - return res; - std::this_thread::sleep_for(std::chrono::seconds(2)); + bool success = + (messageType == MessageType::DATA_MESSAGE) + ? sendPacketWithRTO(packet) // Send with retransmission timeout + : sendPacket(packet); // Send packet directly + if (!success) + return ErrorCode::SEND_FAILED; // Return failure if sending fails } - return ErrorCode::SUCCESS; + return ErrorCode::SUCCESS; // Return success if all packets are sent } // Sends a message Async @@ -72,96 +89,328 @@ void Communication::sendMessageAsync(void *data, size_t dataSize, uint32_t destI sendCallback(res); } -// Accepts the packet from the client and checks.. -void Communication::receivePacket(Packet &p) + +void Communication::receivePacket(Packet &packet) { - if (checkDestId(p)) { - if (validCRC(p)) - handlePacket(p); - else - handleError(); + // Check if the bus is in BusOff state + // If the system is in BusOff state, exit the function and do not process the packet + if (getState() == ErrorState::BusOff) + return; + + // Check if the destination ID of the packet matches the expected destination + if (checkDestId(packet)) { + // Check CRC validity + // Validate the packet's CRC for data integrity + if (!validCRC(packet)) { + // If CRC is invalid, retrieve the packet ID + uint32_t packetId = packet.getId(); + // Send an error message back to the source with the invalid packet ID + sendMessage(&packetId, sizeof(packetId), packet.getSrcId(), + packet.getDestId(), MessageType::ERROR_MESSAGE); + return; // Exit after sending the error message + } + + // Handle message based on type + // Determine the type of the message based on the packet ID + switch (Message::getMessageTypeFromID(packet.getId())) { + case DATA_MESSAGE: + // If the message is a data message, handle it accordingly + handleDataPacket(packet); + break; + case ERROR_MESSAGE: + // If the message is an error message, handle it accordingly + handleErrorPacket(packet); + break; + case ACK: + // If the message is an acknowledgment message, handle it accordingly + handleAckPacket(packet); + break; + default: + // If the message type is unrecognized, do nothing + break; + } } } -// Checks if the packet is intended for him bool Communication::checkDestId(Packet &p) { - return p.getIsBroadcast() || p.getDestId() == this->id; + // If the packet is a broadcast, it can be received by any node + //Or if the destination ID matches, the packet is intended for this node + return p.getIsBroadcast() || p.getDestId() == id; } -// Checks if the data is currect -bool Communication::validCRC(Packet &p) +bool Communication::validCRC(Packet &packet) { - return p.validateCRC(); + // Validate the CRC of the packet + // This checks the integrity of the packet to ensure it hasn't been corrupted + return packet.validateCRC(); } -// Receives the packet and adds it to the message -void Communication::handlePacket(Packet &p) +void Communication::handleDataPacket(Packet &packet) { - // Send acknowledgment according to CAN bus - // client.sendPacket(hadArrived()); - addPacketToMessage(p); + // Add the incoming data packet to the message queue + addPacketToMessage(packet); + + // Retrieve the packet ID for acknowledgment + uint32_t packetId = packet.getId(); + + // Send an acknowledgment message back to the source of the packet + sendMessage(&packetId, sizeof(packetId), packet.getSrcId(), + packet.getDestId(), MessageType::ACK); } -// Implement error handling according to CAN bus -void Communication::handleError() +void Communication::handleErrorPacket(Packet &packet) { - // Handle error cases according to CAN bus + // Check if the error packet is intended for this node + if (packet.getDestId() == id) { + // Handle the transmission error for this node + handleTransmitError(); + } + else { + // Handle the reception error for other nodes + handleReceiveError(); + } } -// Implement arrival confirmation according to the CAN bus -Packet Communication::hadArrived() +void Communication::handleAckPacket(Packet &packet) { - Packet ack; - // Construct and return acknowledgment packet - return ack; + // Retrieve the message ID from the packet's payload + const uint32_t *receivedIdPtr = + reinterpret_cast(packet.getPayload()); + uint32_t receivedId = *receivedIdPtr; + + // Notify the scheduler of the received acknowledgment + scheduler.receiveACK(receivedId); + + // Handle the successful transmission + handleSuccessfulTransmission(); } -// Adding the packet to the complete message -void Communication::addPacketToMessage(Packet &p) +void Communication::addPacketToMessage(Packet &packet) { - std::string messageId = std::to_string(p.getId()); - // If the message already exists, we will add the packet + // Convert the packet ID to a string for lookup + std::string messageId = std::to_string(packet.getId()); + + // Check if the message with the given ID already exists if (receivedMessages.find(messageId) != receivedMessages.end()) { - receivedMessages[messageId].addPacket(p); - } else { - // If the message does not exist, we will create a new message - Message msg(p.getTPS()); - msg.addPacket(p); - receivedMessages[messageId] = msg; + // If it exists, add the packet to the existing message + receivedMessages[messageId].addPacket(packet); + } + else { + // If it does not exist, create a new message and add the packet + Message msg(packet.getTPS()); + msg.addPacket(packet); + receivedMessages[messageId] = msg; // Store the new message } - // If the message is complete, we pass the data to the passData function + // Check if the message is complete if (receivedMessages[messageId].isComplete()) { + // Retrieve complete data from the message void *completeData = receivedMessages[messageId].completeData(); - passData(p.getSrcId(), completeData); - receivedMessages.erase(messageId); // Removing the message once completed + // Pass the complete data for further processing + passData(packet.getSrcId(),completeData); + // Remove the message from the received messages + receivedMessages.erase(messageId); + } +} + +void Communication::recoverFromBusOff() +{ + // Check if the communication system is in the BusOff state + // and if the bus off recovery thread is not already running + if (getState() == ErrorState::BusOff && + (!busOffRecoveryThread.joinable())) { + // Start a new thread for the recovery process + busOffRecoveryThread = std::thread(&Communication::busOffTimer, this); } } -// Static method to handle SIGINT signal +void Communication::busOffTimer() +{ + // Put the thread to sleep for the defined BusOff recovery time + std::this_thread::sleep_for(BUSOFF_RECOVERY_TIME); + // Reset the state of the communication system after the sleep period + resetState(); +} + +void Communication::cleanupBusOffRecoveryThread() +{ + // Check if the BusOff recovery thread is joinable + if (busOffRecoveryThread.joinable()) { + // Join the thread to clean up and ensure proper resource management + busOffRecoveryThread.join(); + } +} + +void Communication::checkState() +{ + // Lock the mutex to ensure thread safety while checking and updating the state + { + std::lock_guard lock(mtx); + // Check if TEC exceeds the BusOff threshold + if (TEC >= THRESHOLD_TO_BUSOFF) { + state = ErrorState::BusOff; // Set state to BusOff + } + // Check if TEC or REC exceeds the Passive threshold + else if (TEC >= THRESHOLD_TO_PASSIVE || REC >= THRESHOLD_TO_PASSIVE) { + state = ErrorState::Passive; // Set state to Passive + } + else { + state = ErrorState::Active; // Set state to Active + } + } + + // If the state is BusOff, initiate recovery process + if (getState() == ErrorState::BusOff) { + recoverFromBusOff(); + } +} + +void Communication::handleTransmitError() +{ + // Lock the mutex to ensure thread safety while updating the TEC + { + std::lock_guard lock(mtx); + TEC += 8; // Increment the Transmission Error Counter by 8 + } + // Check the state based on the updated TEC + checkState(); +} + +void Communication::handleACKError(Packet &packet) +{ + // Lock the mutex to ensure thread safety while updating the TEC + { + std::lock_guard lock(mtx); + TEC += 1; // Increment the Transmission Error Counter by 1 + } + // Check the state based on the updated TEC + checkState(); +} + +void Communication::handleReceiveError() +{ + // Lock the mutex to ensure thread safety while updating the REC + { + std::lock_guard lock(mtx); + REC += 1; // Increment the Reception Error Counter by 1 + } + // Check the state based on the updated REC + checkState(); +} + +void Communication::handleSuccessfulTransmission() +{ + // Lock the mutex to ensure thread safety while updating the TEC + { + std::lock_guard lock(mtx); + if (TEC > 0) + TEC -= + 1; // Decrement the Transmit Error Counter by 1 if greater than zero + } + // Check the state based on the updated TEC + checkState(); +} + +void Communication::resetState() +{ + // Lock the mutex to ensure thread safety while resetting the state + std::lock_guard lock(mtx); + TEC = 0; // Reset the Transmit Error Counter to zero + REC = 0; // Reset the Receive Error Counter to zero + state = ErrorState::Active; // Set the communication state to Active +} + +ErrorState Communication::getState() +{ + // Lock the mutex to ensure thread safety while accessing the state + std::lock_guard lock(mtx); + + // Return the current state of the communication system + return state; // Return the state regardless of its value +} + +bool Communication::sendPacketWithRTO(Packet &packet, + int retransmissions /* = 0 */) +{ + // If the system is in BusOff state or retransmissions exceed the maximum limit, stop sending + if (getState() == ErrorState::BusOff || + retransmissions > MAX_RETRANSMISSIONS) { + // Clear any packet-related data (if needed) if we stop the transmission + scheduler.clearPacketData(packet.getId()); + return false; + } + + // Set the packet to passive mode if the system state is passive (for error handling) + packet.setIsPassive(getState() == ErrorState::Passive); + + // Attempt to send the packet using the client + ErrorCode res = client.sendPacket(packet); + + // If the packet is successfully sent, wait for the ACK + if (res == ErrorCode::SUCCESS) { + // Create a promise to track the success of the transmission (ACK reception) + auto ackPromisePtr = std::make_shared>(); + auto ackFuture = + ackPromisePtr->get_future(); // Future to wait for the ACK result + + // Start a retransmission timer for this packet + scheduler.startRetransmissionTimer( + packet.getId(), + [this, packet, ackPromisePtr](int retryCount) mutable { + // Handle errors if ACK was not received + this->handleACKError(packet); + + // Retry sending the packet and update the promise with the result + bool result = this->sendPacketWithRTO(packet, retryCount); + ackPromisePtr->set_value( + result); // Set the result of retransmission + }, + ackPromisePtr); + + // Wait for the result of the future (ACK reception or timeout) + bool success = ackFuture.get(); + + return success; // Return true if successful, false otherwise + } + + // Return false if the initial packet transmission failed + return false; +} + +bool Communication::sendPacket(Packet &packet) +{ + // Check if the system is in BusOff state + if (getState() == ErrorState::BusOff) + return false; // החזר false מיד + + // Set the packet to passive mode if the system state is passive + packet.setIsPassive(getState() == ErrorState::Passive); + + // Attempt to send the packet using the client + ErrorCode res = client.sendPacket(packet); + + return res == + ErrorCode::SUCCESS; // Return true if successful, false otherwise +} + void Communication::signalHandler(int signum) { - if (instance) - instance->client.closeConnection(); - - exit(signum); + if (instance) { + cleanupBusOffRecoveryThread(); // Clean up the recovery thread if it is running + instance->client.closeConnection(); // Close the client connection + } + exit(signum); // Exit the program with the given signal number } void Communication::setId(uint32_t newId) { - id = newId; + id = newId; // Set the new ID for the communication instance } void Communication::setPassDataCallback(void (*callback)(uint32_t, void *)) { if (callback == nullptr) throw std::invalid_argument("Invalid callback function: passDataCallback cannot be null"); - - passData = callback; -} - -//Destructor -Communication::~Communication() { - instance = nullptr; + passData = callback; // Set the callback function for passing data } \ No newline at end of file From e1a6fca4eab80ede34fd40316778407579170d69 Mon Sep 17 00:00:00 2001 From: tzivya-zalaznik Date: Tue, 1 Oct 2024 14:59:59 +0300 Subject: [PATCH 17/18] Implement asynchronous message sending and limit concurrent transmissions --- communication/include/communication.h | 52 ++++++++++++- communication/src/communication.cpp | 107 ++++++++++++++++++++++---- 2 files changed, 141 insertions(+), 18 deletions(-) diff --git a/communication/include/communication.h b/communication/include/communication.h index 34736d95..85276bf8 100644 --- a/communication/include/communication.h +++ b/communication/include/communication.h @@ -17,6 +17,7 @@ #define THRESHOLD_TO_PASSIVE 128 ///< Threshold for entering Passive state #define BUSOFF_RECOVERY_TIME (128 * TICK_DURATION) ///< Time required to recover from BusOff state #define MAX_RETRANSMISSIONS 10 ///< Maximum allowed retransmissions for a packet +#define MAX_SIMULTANEOUS_MESSAGES 5 ///< Maximum number of messages allowed to be sent simultaneously // Enumeration for different error states of the communication enum class ErrorState { @@ -34,6 +35,15 @@ class Communication { uint32_t id; ///< Unique identifier for the communication instance static Communication *instance; ///< Static variable that holds an instance of the class + // To limit simultaneous messages in asynchronous send + static std::vector> activeMessageFutures; ///< Vector to hold active message futures + static std::mutex messageThreadsMutex; ///< Mutex to synchronize access to the active thread vector + std::condition_variable messageThreadsCondition; ///< Condition variable to wait for available thread slots + + // For ensuring that only one send per tick occurs + std::atomic lastSendTick; ///< The last tick when a packet was sent + std::mutex sendMutex; ///< Mutex for synchronizing send operations + // Error tracking variables std::mutex mtx; ///< Mutex to protect access to error counts and state int TEC; ///< Transmission Error Count @@ -222,6 +232,21 @@ class Communication { */ void resetState(); + //Management of packet transmission according to clock ticks. + + /** + * @brief Ensures that only one message is sent per clock tick. + * + * This method checks the current clock tick and compares it with the last tick + * when a message was sent. If the current tick is different, it updates the + * last sent tick. If it is the same, the function waits for the next tick + * before proceeding. + * + * The method uses a mutex to ensure thread safety during the tick check and + * update operations. + */ + void ensureSingleSendPerTick(); + /** * @brief Sends a packet with a retransmission timeout (RTO). * @@ -229,6 +254,7 @@ class Communication { * number of retransmissions. If the system is in the BusOff state or the * maximum retransmissions have been exceeded, it stops sending the packet. * + * It ensures that only one packet is sent per tick and sets the packet's * passive state based on the current system state. It waits for an acknowledgment * (ACK) and handles retransmissions if needed. * @@ -242,7 +268,7 @@ class Communication { * @brief Sends a packet to the designated recipient. * * This method checks if the system is in the BusOff state. If it is, the function - * immediately returns false. + * immediately returns false. It ensures that only one packet is sent per tick * and sets the packet's passive state based on the current system state. * * The method attempts to send the packet using the client and returns true if @@ -253,6 +279,15 @@ class Communication { */ bool sendPacket(Packet &packet); + /** + * @brief Waits for all active message threads to complete. + * + * This function manages the synchronization of active message threads in a thread-safe manner. + * It locks the mutex to ensure thread safety while iterating through the futures of active message threads. + * Each valid future is waited upon to ensure that all active messages have been processed before clearing the vector. + */ + static void waitForActiveMessages(); + /** * @brief Handles signals for the communication instance. * @@ -335,7 +370,20 @@ class Communication { uint32_t srcID, MessageType messageType = MessageType::DATA_MESSAGE); - // Sends a message to manager - Async + /** + * @brief Sends a message asynchronously. + * + * This function sends a message to a specified destination ID asynchronously, + * allowing the calling thread to continue without blocking. It accepts a callback + * function that will be called with the result of the sending operation. + * + * @param data Pointer to the data to send. + * @param dataSize Size of the data in bytes. + * @param destID The destination ID for the message. + * @param srcID The source ID of the message. + * @param sendCallback The callback function to call with the result of the send operation. + * @param messageType The type of message being sent. + */ void sendMessageAsync(void *data, size_t dataSize, uint32_t destID, uint32_t srcID, std::function sendCallback, diff --git a/communication/src/communication.cpp b/communication/src/communication.cpp index 60ae04d7..c9134149 100644 --- a/communication/src/communication.cpp +++ b/communication/src/communication.cpp @@ -3,12 +3,15 @@ // Initialize static member variables for the Communication class Communication *Communication::instance = nullptr; std::thread Communication::busOffRecoveryThread; +std::mutex Communication::messageThreadsMutex; +std::vector> Communication::activeMessageFutures; // Constructor Communication::Communication(uint32_t id, void (*passDataCallback)(uint32_t, void *)) : TEC(0), REC(0), state(ErrorState::Active), client(std::bind(&Communication::receivePacket, this, - std::placeholders::_1)) + std::placeholders::_1)), + lastSendTick(GlobalClock::getCurrentTick()) { setId(id); // Set the communication ID setPassDataCallback(passDataCallback); // Set the callback for data passing @@ -25,6 +28,7 @@ Communication::Communication(uint32_t id, void (*passDataCallback)(uint32_t, voi Communication::~Communication() { cleanupBusOffRecoveryThread(); // Clean up the bus off recovery thread + waitForActiveMessages(); // Wait for any active message threads to complete instance->client.closeConnection(); // Close the client connection instance = nullptr; // Clear the singleton instance } @@ -72,24 +76,49 @@ ErrorCode Communication::sendMessage( return ErrorCode::SUCCESS; // Return success if all packets are sent } -// Sends a message Async -void Communication::sendMessageAsync(void *data, size_t dataSize, uint32_t destID, - uint32_t srcID, - std::function sendCallback, - MessageType messageType) +// Send message asynchronously +void Communication::sendMessageAsync( + void *data, size_t dataSize, uint32_t destID, uint32_t srcID, + std::function sendCallback, MessageType messageType) { - std::promise resultPromise; - std::future resultFuture = resultPromise.get_future(); - - std::thread([this, data, dataSize, destID, srcID, messageType, sendCallback,&resultPromise]() { - ErrorCode result = this->sendMessage(data, dataSize, destID, srcID, messageType); // Send the message resultPromise.set_value(res); - }).detach(); - - ErrorCode res = resultFuture.get(); - sendCallback(res); + std::unique_lock lock(messageThreadsMutex); + // Wait until the number of active threads is below the maximum limit + while (activeMessageFutures.size() >= MAX_SIMULTANEOUS_MESSAGES) { + messageThreadsCondition.wait(lock); // Wait for a thread to complete + } + // Create a new promise and future + std::promise promise; + std::future future = promise.get_future(); + activeMessageFutures.push_back(std::move(future)); // Store future + + // Create a new thread to handle message sending + std::thread([this, data, dataSize, destID, srcID, messageType, sendCallback, + promise = std::move(promise)]() mutable { + ErrorCode result = this->sendMessage(data, dataSize, destID, srcID, + messageType); // Send the message + sendCallback(result); // Call the callback after the message is sent + + // Notify that the thread work is done + promise.set_value(); // Set the promise value + + // Lock the mutex to modify the active thread list + std::unique_lock lock(this->messageThreadsMutex); + + // Remove the promise from the vector of active futures + auto it = std::remove_if( + activeMessageFutures.begin(), activeMessageFutures.end(), + [](std::future &future) { + return future.wait_for(std::chrono::seconds(0)) == + std::future_status:: + ready; // Check if the future is ready + }); + activeMessageFutures.erase( + it, activeMessageFutures.end()); // Remove invalid futures + // Notify one waiting thread that space is available + this->messageThreadsCondition.notify_one(); + }).detach(); // Detach the thread } - void Communication::receivePacket(Packet &packet) { // Check if the bus is in BusOff state @@ -330,6 +359,31 @@ ErrorState Communication::getState() return state; // Return the state regardless of its value } +void Communication::ensureSingleSendPerTick() +{ + // Lock the mutex to ensure thread safety while checking the tick + std::lock_guard lock(sendMutex); // ננעל את המנעול + + // Retrieve the current clock tick from the GlobalClock + int currentTick = + GlobalClock::getCurrentTick(); // קבל את פעימת השעון הנוכחית + + // Check if the current tick is different from the last sent tick + if (currentTick != lastSendTick.load()) { + // Update the last sent tick to the current tick + lastSendTick.store(currentTick); // עדכן את lastSendTick + + return; // Exit the function if a message can be sent + } + + // Wait for the next clock tick if the last tick is the same + GlobalClock::waitForNextTick(); + + // Update lastSendTick after waiting + lastSendTick.store( + GlobalClock::getCurrentTick()); // עדכון lastSendTick לאחר ההמתנה +} + bool Communication::sendPacketWithRTO(Packet &packet, int retransmissions /* = 0 */) { @@ -341,6 +395,8 @@ bool Communication::sendPacketWithRTO(Packet &packet, return false; } + ensureSingleSendPerTick(); + // Set the packet to passive mode if the system state is passive (for error handling) packet.setIsPassive(getState() == ErrorState::Passive); @@ -384,6 +440,8 @@ bool Communication::sendPacket(Packet &packet) if (getState() == ErrorState::BusOff) return false; // החזר false מיד + ensureSingleSendPerTick(); + // Set the packet to passive mode if the system state is passive packet.setIsPassive(getState() == ErrorState::Passive); @@ -394,10 +452,27 @@ bool Communication::sendPacket(Packet &packet) ErrorCode::SUCCESS; // Return true if successful, false otherwise } +void Communication::waitForActiveMessages() +{ + std::unique_lock lock( + messageThreadsMutex); // Lock for thread safety + + // Wait for all active message threads to complete + for (auto &future : activeMessageFutures) { + if (future.valid()) { + future.wait(); // Wait for the future to finish if valid + } + } + + activeMessageFutures + .clear(); // Clear the vector after all futures are completed +} + void Communication::signalHandler(int signum) { if (instance) { cleanupBusOffRecoveryThread(); // Clean up the recovery thread if it is running + waitForActiveMessages(); // Wait for any active message threads to complete instance->client.closeConnection(); // Close the client connection } exit(signum); // Exit the program with the given signal number From 73c3cafe351edb93a1d6d4849481eb5236cdcf6e Mon Sep 17 00:00:00 2001 From: Shoshi Date: Mon, 7 Oct 2024 20:08:29 +0300 Subject: [PATCH 18/18] Added logging for functionality simulating CAN BUS protocol --- communication/include/communication.h | 2 +- communication/include/global_clock.h | 1 + communication/include/message.h | 6 + communication/include/packet.h | 5 +- communication/include/scheduler.h | 2 +- communication/sockets/real_socket.cpp | 22 +-- communication/sockets/real_socket.h | 1 + communication/src/bus_manager.cpp | 46 +++++- communication/src/communication.cpp | 222 +++++++++++++++++++++----- communication/src/global_clock.cpp | 94 +++++------ communication/src/packet.cpp | 13 +- communication/src/scheduler.cpp | 23 ++- 12 files changed, 303 insertions(+), 134 deletions(-) diff --git a/communication/include/communication.h b/communication/include/communication.h index 85276bf8..20c240d4 100644 --- a/communication/include/communication.h +++ b/communication/include/communication.h @@ -201,7 +201,7 @@ class Communication { * * @param packet The packet object associated with the acknowledgment error. */ - void handleACKError(Packet &packet); + void handleACKError(); /** * @brief Handles reception errors by updating the Reception Error Counter (REC). diff --git a/communication/include/global_clock.h b/communication/include/global_clock.h index 7ced3b6f..34a1054c 100644 --- a/communication/include/global_clock.h +++ b/communication/include/global_clock.h @@ -11,6 +11,7 @@ #include #include #include +#include "../sockets/real_socket.h" #define SHM_NAME "/clock__shm" #define TICK_DURATION std::chrono::milliseconds(50) // Define the tick duration diff --git a/communication/include/message.h b/communication/include/message.h index 5916e4f9..d3016794 100644 --- a/communication/include/message.h +++ b/communication/include/message.h @@ -55,4 +55,10 @@ class Message { // Returns a string representation of the MessageType enum value static std::string getMessageTypeString(MessageType type); + + // Get the message ID + uint32_t getMessageID() const + { + return messageID; + } }; \ No newline at end of file diff --git a/communication/include/packet.h b/communication/include/packet.h index 4e4947ae..d8ce6120 100644 --- a/communication/include/packet.h +++ b/communication/include/packet.h @@ -50,9 +50,8 @@ class Packet { uint16_t calculateCRC() const; bool validateCRC() const; - // A function to convert the data to hexa (logger) - std::string pointerToHex() const; - + // Static function to convert raw data to hexadecimal + static std::string pointerToHex(const void* data, size_t size); private: // Header structure within Packet struct Header { diff --git a/communication/include/scheduler.h b/communication/include/scheduler.h index 9167c4ef..9b1c72b5 100644 --- a/communication/include/scheduler.h +++ b/communication/include/scheduler.h @@ -9,7 +9,7 @@ #include #include #include "global_clock.h" - +#include "../sockets/real_socket.h" // must be set #define MAX_ACK_TIMEOUT \ (20 * TICK_DURATION) ///< Maximum time to wait for ACK diff --git a/communication/sockets/real_socket.cpp b/communication/sockets/real_socket.cpp index 530d33b0..688f07f5 100644 --- a/communication/sockets/real_socket.cpp +++ b/communication/sockets/real_socket.cpp @@ -86,14 +86,9 @@ ssize_t RealSocket::recv(int sockfd, void *buf, size_t len, int flags) RealSocket::log.logMessage(logger::LogLevel::ERROR, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), std::string(" Error occurred: in socket ") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); else if (valread == 0) RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), std::string(" connection closed: in socket ") + std::to_string(sockfd) + std::string(" ") + std::string(strerror(errno))); - else { - if (!p->getDLC()) - RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), std::string("received packet number: ") + std::to_string(p->getPSN()) + std::string(", of messageId: ") + std::to_string(p->getId()) + std::string(" ") + std::string(strerror(errno)) + " ID for connection: " + std::to_string(p->getSrcId())); - else - RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), std::string("received packet number: ") + std::to_string(p->getPSN()) + std::string(", of messageId: ") + std::to_string(p->getId()) + std::string(" ") + std::string(strerror(errno)) + " Data: " + p->pointerToHex()); - } + else + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), std::string("received packet type: ") + Message::getMessageTypeString(Message::getMessageTypeFromID(p->getId())) +std::string(" number: ") + std::to_string(p->getPSN()) + std::string(", of messageId: ") + std::to_string(p->getId()) + std::string(" ") + std::string(strerror(errno)) + ( p->getDLC() ? " Data: " + Packet::pointerToHex(p->getPayload(),p->getDLC()):"")); - return valread; } @@ -102,16 +97,13 @@ ssize_t RealSocket::send(int sockfd, const void *buf, size_t len, int flags) int sendAns = ::send(sockfd, buf, len, flags); const Packet *p = static_cast(buf); if (sendAns <= 0) - { - RealSocket::log.logMessage(logger::LogLevel::ERROR, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), "sending packet number: " + std::to_string(p->getPSN()) + ", of messageId: " + std::to_string(p->getId()) + std::string(" ") + std::string(strerror(errno))); - } - if (!p->getDLC()) - RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), "sending packet number: " + std::to_string(p->getPSN()) + ", of messageId: " + std::to_string(p->getId()) + std::string(" ") + std::string(strerror(errno)) + " ID for connection: " + std::to_string(p->getSrcId())); + RealSocket::log.logMessage(logger::LogLevel::ERROR, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), "Error occurred while sending packet type: " + Message::getMessageTypeString(Message::getMessageTypeFromID(p->getId())) + ", number: " + std::to_string(p->getPSN()) + " ,of messageId: " + std::to_string(p->getId()) + ", error: " + std::string(strerror(errno))); else - RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), "sending packet number: " + std::to_string(p->getPSN()) + ", of messageId: " + std::to_string(p->getId()) + std::string(" ") + std::string(strerror(errno)) + " Data: " + p->pointerToHex()); - return sendAns; -} + RealSocket::log.logMessage(logger::LogLevel::INFO, std::to_string(p->getSrcId()), std::to_string(p->getDestId()), "sending packet type: " + Message::getMessageTypeString(Message::getMessageTypeFromID(p->getId())) + ", number: " + std::to_string(p->getPSN()) + " ,of messageId: " + std::to_string(p->getId()) + (p->getDLC() ? ", Data: " + Packet::pointerToHex(p->getPayload(), p->getDLC()) : "")); + return sendAns; +} + int RealSocket::close(int fd) { RealSocket::log.logMessage(logger::LogLevel::INFO, "close socket number: " + std::to_string(fd)); diff --git a/communication/sockets/real_socket.h b/communication/sockets/real_socket.h index f9eecb51..9f89ca92 100644 --- a/communication/sockets/real_socket.h +++ b/communication/sockets/real_socket.h @@ -5,6 +5,7 @@ #include #include #include "../include/packet.h" +#include "../include/message.h" class RealSocket : public ISocket { diff --git a/communication/src/bus_manager.cpp b/communication/src/bus_manager.cpp index 79ebaad9..55da6240 100644 --- a/communication/src/bus_manager.cpp +++ b/communication/src/bus_manager.cpp @@ -59,13 +59,42 @@ void BusManager::checkCollision(Packet ¤tPacket) } else { if (lastPacket->getTimestamp() == currentPacket.getTimestamp()) { + RealSocket::log.logMessage( + logger::LogLevel::INFO, "Handled collision between packet from SRC " + + std::to_string(lastPacket->getSrcId()) + + " to DST " + std::to_string(lastPacket->getDestId()) + + ", (packet number: " + std::to_string(lastPacket->getPSN()) + + " of messageId " + std::to_string(lastPacket->getId()) + + ") and packet from SRC " + std::to_string(currentPacket.getSrcId()) + + " to DST " + std::to_string(currentPacket.getDestId()) + + ", (packet number: " + std::to_string(currentPacket.getPSN()) + + " of messageId " + std::to_string(currentPacket.getId()) + + ")." + ); + // Same timestamp indicates potential collision, check priority - Packet *prioritizedPacket = - packetPriority(*lastPacket, currentPacket); + Packet* prioritizedPacket = packetPriority(*lastPacket, currentPacket); + + // Log the result once and determine the lost packet + Packet* lostPacket = (prioritizedPacket == ¤tPacket) ? lastPacket : ¤tPacket; + + // Update the last packet if current has priority if (prioritizedPacket == ¤tPacket) { - delete lastPacket; // Replace last packet if current packet has priority + delete lastPacket; // Replace last packet lastPacket = new Packet(currentPacket); } + + RealSocket::log.logMessage( + logger::LogLevel::INFO, "Packet from SRC " + + std::to_string(prioritizedPacket->getSrcId()) + + " to DST " + std::to_string(prioritizedPacket->getDestId()) + + ", (packet number: " + std::to_string(prioritizedPacket->getPSN()) + + " of messageId " + std::to_string(prioritizedPacket->getId()) + + " ) won the collision. Packet from SRC " + std::to_string(lostPacket->getSrcId()) + + " to DST " + std::to_string(lostPacket->getDestId()) + + ", (packet number: " + std::to_string(lostPacket->getPSN()) + + " of messageId " + std::to_string(lostPacket->getId()) + ") was lost." + ); } } } @@ -89,16 +118,21 @@ Packet *BusManager::packetPriority(Packet &a, Packet &b) void BusManager::startCollisionTimer() { stopFlag = false; + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Starting collision timer thread."); // לוג לפני התחלת ה-thread + collisionTimerThread = std::thread([this]() { while (!stopFlag) { - GlobalClock::waitForNextTick(); - if (!stopFlag) { + GlobalClock::waitForNextTick(); // מחכה לשעון + + if (!stopFlag) checkLastPacket(); // Check and send last packet if necessary - } } + + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Collision timer thread stopping."); // לוג כאשר ה-thread מפסיק }); } + // Checks the last packet and sends it if it hasn't been sent yet void BusManager::checkLastPacket() { diff --git a/communication/src/communication.cpp b/communication/src/communication.cpp index c9134149..f803c498 100644 --- a/communication/src/communication.cpp +++ b/communication/src/communication.cpp @@ -62,16 +62,34 @@ ErrorCode Communication::sendMessage( messageType); // Create a new message //Sending the message to logger - RealSocket::log.logMessage(logger::LogLevel::INFO,std::to_string(srcID),std::to_string(destID),"Complete message:" + msg.getPackets().at(0).pointerToHex()); - + RealSocket::log.logMessage(logger::LogLevel::INFO, "Start sending " + + Message::getMessageTypeString(messageType) + + " message with ID: " + + std::to_string(msg.getMessageID()) + + " Complete message:" + +Packet::pointerToHex(data ,dataSize)); + for (auto &packet : msg.getPackets()) { bool success = (messageType == MessageType::DATA_MESSAGE) ? sendPacketWithRTO(packet) // Send with retransmission timeout : sendPacket(packet); // Send packet directly - if (!success) + if (!success) { + if( messageType == MessageType::DATA_MESSAGE) + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Failed sending " + + Message::getMessageTypeString(messageType) + + " message with ID: " + + std::to_string(msg.getMessageID()) + "."); + else + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Failed sending messageID: " + std::to_string(msg.getMessageID())); return ErrorCode::SEND_FAILED; // Return failure if sending fails + } } + RealSocket::log.logMessage(logger::LogLevel::INFO, "Finished sending " + + Message::getMessageTypeString(messageType) + + " message with ID: " + + std::to_string(msg.getMessageID()) + + " successfully."); return ErrorCode::SUCCESS; // Return success if all packets are sent } @@ -84,8 +102,14 @@ void Communication::sendMessageAsync( std::unique_lock lock(messageThreadsMutex); // Wait until the number of active threads is below the maximum limit while (activeMessageFutures.size() >= MAX_SIMULTANEOUS_MESSAGES) { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Maximum simultaneous messages reached. Waiting for available thread..."); messageThreadsCondition.wait(lock); // Wait for a thread to complete } + // Print the number of active threads (i.e. messages being sent) + RealSocket::log.logMessage(logger::LogLevel::DEBUG, + "Number of messages being sent simultaneously: " + + std::to_string(activeMessageFutures.size() + 1)); // +1 to include the new thread + // Create a new promise and future std::promise promise; std::future future = promise.get_future(); @@ -123,19 +147,39 @@ void Communication::receivePacket(Packet &packet) { // Check if the bus is in BusOff state // If the system is in BusOff state, exit the function and do not process the packet - if (getState() == ErrorState::BusOff) + if (getState() == ErrorState::BusOff) { + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Cannot receive packet " + + std::to_string(packet.getPSN()) + + ",of message ID: " + + std::to_string(packet.getId()) + + ", system in BusOff state."); return; + } // Check if the destination ID of the packet matches the expected destination if (checkDestId(packet)) { // Check CRC validity // Validate the packet's CRC for data integrity if (!validCRC(packet)) { - // If CRC is invalid, retrieve the packet ID + // Log the CRC error with packet ID and other information + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Invalid CRC detected for packet " + + std::to_string(packet.getPSN()) + + " of message ID: " + + std::to_string(packet.getId()) + "."); + // Retrieve the packet ID uint32_t packetId = packet.getId(); // Send an error message back to the source with the invalid packet ID sendMessage(&packetId, sizeof(packetId), packet.getSrcId(), packet.getDestId(), MessageType::ERROR_MESSAGE); + + RealSocket::log.logMessage(logger::LogLevel::INFO, "Sending ERROR message for packet " + + std::to_string(packet.getPSN()) + + "of message ID " + + std::to_string(packetId) + + " back to source ID " + + std::to_string(packet.getSrcId()) + + " (Broadcast)."); + return; // Exit after sending the error message } @@ -177,6 +221,11 @@ bool Communication::validCRC(Packet &packet) void Communication::handleDataPacket(Packet &packet) { + RealSocket::log.logMessage(logger::LogLevel::INFO, "Handling data packet " + + std::to_string(packet.getPSN()) + + " of message ID " + + std::to_string(packet.getId()) + "."); + // Add the incoming data packet to the message queue addPacketToMessage(packet); @@ -184,12 +233,22 @@ void Communication::handleDataPacket(Packet &packet) uint32_t packetId = packet.getId(); // Send an acknowledgment message back to the source of the packet - sendMessage(&packetId, sizeof(packetId), packet.getSrcId(), - packet.getDestId(), MessageType::ACK); + RealSocket::log.logMessage(logger::LogLevel::INFO, "Sending ACK for packet " + + std::to_string(packet.getPSN()) + + " of message ID " + + std::to_string(packetId) + + " back to source ID " + + std::to_string(packet.getSrcId()) + "."); + sendMessage(&packetId, sizeof(packetId), packet.getSrcId(), packet.getDestId(), MessageType::ACK); } void Communication::handleErrorPacket(Packet &packet) { + RealSocket::log.logMessage(logger::LogLevel::INFO, "Handling error packet " + + std::to_string(packet.getPSN()) + + " of message ID " + + std::to_string(packet.getId()) + "."); + // Check if the error packet is intended for this node if (packet.getDestId() == id) { // Handle the transmission error for this node @@ -203,9 +262,13 @@ void Communication::handleErrorPacket(Packet &packet) void Communication::handleAckPacket(Packet &packet) { + RealSocket::log.logMessage(logger::LogLevel::INFO, "Handling ACK packet " + + std::to_string(packet.getPSN()) + + " of message ID " + + std::to_string(packet.getId()) + "."); + // Retrieve the message ID from the packet's payload - const uint32_t *receivedIdPtr = - reinterpret_cast(packet.getPayload()); + const uint32_t *receivedIdPtr = reinterpret_cast(packet.getPayload()); uint32_t receivedId = *receivedIdPtr; // Notify the scheduler of the received acknowledgment @@ -223,10 +286,19 @@ void Communication::addPacketToMessage(Packet &packet) // Check if the message with the given ID already exists if (receivedMessages.find(messageId) != receivedMessages.end()) { // If it exists, add the packet to the existing message + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Adding packet " + + std::to_string(packet.getPSN()) + + " to existing message ID " + + messageId + "."); receivedMessages[messageId].addPacket(packet); } else { // If it does not exist, create a new message and add the packet + RealSocket::log.logMessage(logger::LogLevel::INFO, "Creating a new message for receiving message ID " + + std::to_string(packet.getId()) + + " and adding packet " + + std::to_string(packet.getPSN()) + + " to it."); Message msg(packet.getTPS()); msg.addPacket(packet); receivedMessages[messageId] = msg; // Store the new message @@ -234,10 +306,13 @@ void Communication::addPacketToMessage(Packet &packet) // Check if the message is complete if (receivedMessages[messageId].isComplete()) { + RealSocket::log.logMessage(logger::LogLevel::INFO, "Message ID " + + messageId + + " is complete. Processing complete data."); // Retrieve complete data from the message void *completeData = receivedMessages[messageId].completeData(); // Pass the complete data for further processing - passData(packet.getSrcId(),completeData); + passData(packet.getSrcId(), completeData); // Remove the message from the received messages receivedMessages.erase(messageId); } @@ -249,6 +324,7 @@ void Communication::recoverFromBusOff() // and if the bus off recovery thread is not already running if (getState() == ErrorState::BusOff && (!busOffRecoveryThread.joinable())) { + RealSocket::log.logMessage(logger::LogLevel::INFO, "Recovering from BusOff state."); // Start a new thread for the recovery process busOffRecoveryThread = std::thread(&Communication::busOffTimer, this); } @@ -256,14 +332,19 @@ void Communication::recoverFromBusOff() void Communication::busOffTimer() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "BusOff recovery started, sleeping for " + std::to_string(BUSOFF_RECOVERY_TIME.count()) + " milliseconds."); + // Put the thread to sleep for the defined BusOff recovery time std::this_thread::sleep_for(BUSOFF_RECOVERY_TIME); + + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "BusOff recovery time elapsed. Resetting communication state."); // Reset the state of the communication system after the sleep period resetState(); } void Communication::cleanupBusOffRecoveryThread() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Cleaning up BusOff recovery thread."); // Check if the BusOff recovery thread is joinable if (busOffRecoveryThread.joinable()) { // Join the thread to clean up and ensure proper resource management @@ -273,18 +354,24 @@ void Communication::cleanupBusOffRecoveryThread() void Communication::checkState() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Checking communication state. TEC: " + std::to_string(TEC) + ", REC: " + std::to_string(REC)); + // Lock the mutex to ensure thread safety while checking and updating the state { std::lock_guard lock(mtx); + // Check if TEC exceeds the BusOff threshold if (TEC >= THRESHOLD_TO_BUSOFF) { + RealSocket::log.logMessage(logger::LogLevel::INFO, "State changed to BusOff. TEC exceeded threshold: " + std::to_string(TEC)); state = ErrorState::BusOff; // Set state to BusOff } // Check if TEC or REC exceeds the Passive threshold else if (TEC >= THRESHOLD_TO_PASSIVE || REC >= THRESHOLD_TO_PASSIVE) { + RealSocket::log.logMessage(logger::LogLevel::INFO, "State changed to Passive. TEC or REC exceeded threshold."); state = ErrorState::Passive; // Set state to Passive } else { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "State remains Active."); state = ErrorState::Active; // Set state to Active } } @@ -297,107 +384,126 @@ void Communication::checkState() void Communication::handleTransmitError() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Handling transmission error. Incrementing TEC by 8."); // Lock the mutex to ensure thread safety while updating the TEC { std::lock_guard lock(mtx); TEC += 8; // Increment the Transmission Error Counter by 8 } + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "TEC after increment: " + std::to_string(TEC)); // Check the state based on the updated TEC checkState(); } -void Communication::handleACKError(Packet &packet) +void Communication::handleACKError() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Handling ACK error. Incrementing TEC by 1."); // Lock the mutex to ensure thread safety while updating the TEC { std::lock_guard lock(mtx); TEC += 1; // Increment the Transmission Error Counter by 1 } + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "TEC after increment: " + std::to_string(TEC)); // Check the state based on the updated TEC checkState(); } void Communication::handleReceiveError() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Handling reception error. Incrementing REC by 1."); // Lock the mutex to ensure thread safety while updating the REC { std::lock_guard lock(mtx); REC += 1; // Increment the Reception Error Counter by 1 } + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "REC after increment: " + std::to_string(REC)); // Check the state based on the updated REC checkState(); } void Communication::handleSuccessfulTransmission() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Handling successful transmission. Decrementing TEC by 1 if greater than zero."); // Lock the mutex to ensure thread safety while updating the TEC { std::lock_guard lock(mtx); if (TEC > 0) - TEC -= - 1; // Decrement the Transmit Error Counter by 1 if greater than zero + TEC -= 1; // Decrement the Transmit Error Counter by 1 if greater than zero } + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "TEC after decrement: " + std::to_string(TEC)); // Check the state based on the updated TEC checkState(); } void Communication::resetState() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Resetting communication state. Resetting TEC, REC, and setting state to Active."); // Lock the mutex to ensure thread safety while resetting the state std::lock_guard lock(mtx); TEC = 0; // Reset the Transmit Error Counter to zero REC = 0; // Reset the Receive Error Counter to zero state = ErrorState::Active; // Set the communication state to Active + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "State reset complete. TEC: 0, REC: 0, State: Active."); } ErrorState Communication::getState() { // Lock the mutex to ensure thread safety while accessing the state std::lock_guard lock(mtx); - // Return the current state of the communication system return state; // Return the state regardless of its value } void Communication::ensureSingleSendPerTick() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Ensuring single send per tick."); + // Lock the mutex to ensure thread safety while checking the tick - std::lock_guard lock(sendMutex); // ננעל את המנעול + std::lock_guard lock(sendMutex); // Retrieve the current clock tick from the GlobalClock - int currentTick = - GlobalClock::getCurrentTick(); // קבל את פעימת השעון הנוכחית + int currentTick = GlobalClock::getCurrentTick(); // Check if the current tick is different from the last sent tick if (currentTick != lastSendTick.load()) { // Update the last sent tick to the current tick - lastSendTick.store(currentTick); // עדכן את lastSendTick + lastSendTick.store(currentTick); + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Message can be sent this tick."); return; // Exit the function if a message can be sent } // Wait for the next clock tick if the last tick is the same + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Waiting for next clock tick."); GlobalClock::waitForNextTick(); // Update lastSendTick after waiting - lastSendTick.store( - GlobalClock::getCurrentTick()); // עדכון lastSendTick לאחר ההמתנה + lastSendTick.store(GlobalClock::getCurrentTick()); } -bool Communication::sendPacketWithRTO(Packet &packet, - int retransmissions /* = 0 */) +bool Communication::sendPacketWithRTO(Packet &packet, int retransmissions /* = 0 */) { + RealSocket::log.logMessage(logger::LogLevel::INFO, "Sending packet " + + std::to_string(packet.getPSN()) + + ", with RTO of message ID: " + + std::to_string(packet.getId()) + + ", Retransmissions: " + + std::to_string(retransmissions) + "."); + // If the system is in BusOff state or retransmissions exceed the maximum limit, stop sending - if (getState() == ErrorState::BusOff || - retransmissions > MAX_RETRANSMISSIONS) { - // Clear any packet-related data (if needed) if we stop the transmission + if (getState() == ErrorState::BusOff || retransmissions > MAX_RETRANSMISSIONS) { + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Cannot send packet " + + std::to_string(packet.getPSN()) + + ",of message ID: " + + std::to_string(packet.getId()) + + ", State: BusOff or retransmissions exceeded limit."); scheduler.clearPacketData(packet.getId()); return false; } ensureSingleSendPerTick(); - // Set the packet to passive mode if the system state is passive (for error handling) + // Set the packet to passive mode if the system state is passive packet.setIsPassive(getState() == ErrorState::Passive); // Attempt to send the packet using the client @@ -405,40 +511,65 @@ bool Communication::sendPacketWithRTO(Packet &packet, // If the packet is successfully sent, wait for the ACK if (res == ErrorCode::SUCCESS) { + RealSocket::log.logMessage(logger::LogLevel::INFO, "Packet " + + std::to_string(packet.getPSN()) + + ", of message ID: " + std::to_string(packet.getId()) + + " sent. Waiting for ACK..."); + // Create a promise to track the success of the transmission (ACK reception) auto ackPromisePtr = std::make_shared>(); - auto ackFuture = - ackPromisePtr->get_future(); // Future to wait for the ACK result + auto ackFuture = ackPromisePtr->get_future(); // Future to wait for the ACK result // Start a retransmission timer for this packet scheduler.startRetransmissionTimer( packet.getId(), [this, packet, ackPromisePtr](int retryCount) mutable { // Handle errors if ACK was not received - this->handleACKError(packet); + this->handleACKError(); // Retry sending the packet and update the promise with the result bool result = this->sendPacketWithRTO(packet, retryCount); - ackPromisePtr->set_value( - result); // Set the result of retransmission + ackPromisePtr->set_value(result); // Set the result of retransmission }, ackPromisePtr); // Wait for the result of the future (ACK reception or timeout) bool success = ackFuture.get(); + RealSocket::log.logMessage(logger::LogLevel::INFO, + std::string(success ? "ACK received successfully" : "ACK reception failed") + + " for packet " + + std::to_string(packet.getPSN()) + + ", of message ID: " + + std::to_string(packet.getId()) + "."); + return success; // Return true if successful, false otherwise } - // Return false if the initial packet transmission failed + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Packet " + + std::to_string(packet.getPSN()) + + ", of message ID: " + std::to_string(packet.getId()) + + " transmission failed."); + // Return false if the packet transmission failed return false; } bool Communication::sendPacket(Packet &packet) { + RealSocket::log.logMessage(logger::LogLevel::INFO, "Sending packet " + + std::to_string(packet.getPSN()) + + ", of message ID: " + + std::to_string(packet.getId()) + "."); + // Check if the system is in BusOff state - if (getState() == ErrorState::BusOff) - return false; // החזר false מיד + if (getState() == ErrorState::BusOff) { + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Cannot send packet " + + std::to_string(packet.getPSN()) + + ",of message ID: " + + std::to_string(packet.getId()) + + ", system in BusOff state."); + return false; // Return false immediately + } ensureSingleSendPerTick(); @@ -448,14 +579,25 @@ bool Communication::sendPacket(Packet &packet) // Attempt to send the packet using the client ErrorCode res = client.sendPacket(packet); - return res == - ErrorCode::SUCCESS; // Return true if successful, false otherwise + res == ErrorCode::SUCCESS ? + RealSocket::log.logMessage(logger::LogLevel::INFO, "Packet " + + std::to_string(packet.getPSN()) + + ", of message ID: " + + std::to_string(packet.getId()) + + " sent successfully."): + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Packet " + + std::to_string(packet.getPSN()) + + ", of message ID: " + + std::to_string(packet.getId()) + + " Failed to send."); + return res == ErrorCode::SUCCESS; // Return true if successful, false otherwise } void Communication::waitForActiveMessages() { - std::unique_lock lock( - messageThreadsMutex); // Lock for thread safety + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Waiting for active messages to complete..."); + + std::unique_lock lock(messageThreadsMutex); // Lock for thread safety // Wait for all active message threads to complete for (auto &future : activeMessageFutures) { @@ -464,8 +606,8 @@ void Communication::waitForActiveMessages() } } - activeMessageFutures - .clear(); // Clear the vector after all futures are completed + activeMessageFutures.clear(); // Clear the vector after all futures are completed + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "All active messages completed."); } void Communication::signalHandler(int signum) diff --git a/communication/src/global_clock.cpp b/communication/src/global_clock.cpp index 30bb20e9..e4dee8ba 100644 --- a/communication/src/global_clock.cpp +++ b/communication/src/global_clock.cpp @@ -7,63 +7,58 @@ std::thread GlobalClock::clock_thread; void GlobalClock::startClock() { initializeSharedMemory(); // Initialize shared memory if not done yet - if (shared_clock->is_running - .load()) { // Check if the clock is already running - std::cerr << "Clock is already running." << std::endl; + if (shared_clock->is_running.load()) { // Check if the clock is already running + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Clock is already running."); return; // Exit if clock is running } - initializeClockSynchronization(); //Initialize the mutex and condition variables for shared clock synchronization. + initializeClockSynchronization(); // Initialize the mutex and condition variables for shared clock synchronization. shared_clock->is_running.store(true); // Set the clock state to running + RealSocket::log.logMessage(logger::LogLevel::INFO, "Clock started."); clock_thread = std::thread(runClock); // Run the clock in a new thread } int GlobalClock::getCurrentTick() { initializeSharedMemory(); // Ensure shared memory is initialized before accessing the tick - return shared_clock->current_tick.load(); // Return the current tick value + int currentTick = shared_clock->current_tick.load(); + return currentTick; // Return the current tick value } void GlobalClock::waitForNextTick() { initializeSharedMemory(); // Ensure shared memory is initialized - pthread_mutex_lock( - &shared_clock->tick_mutex); // Lock the mutex to access the tick safely - pthread_cond_wait( - &shared_clock->tick_cond, - &shared_clock->tick_mutex); // Wait for tick condition signal - pthread_mutex_unlock( - &shared_clock->tick_mutex); // Unlock the mutex after tick change + pthread_mutex_lock(&shared_clock->tick_mutex); // Lock the mutex to access the tick safely + pthread_cond_wait(&shared_clock->tick_cond, &shared_clock->tick_mutex); // Wait for tick condition signal + pthread_mutex_unlock(&shared_clock->tick_mutex); // Unlock the mutex after tick change } void GlobalClock::stopClock() { if (shared_clock->is_running.load()) { + RealSocket::log.logMessage(logger::LogLevel::INFO, "Stopping the clock..."); shared_clock->is_running.store(false); // Stop the clock if (clock_thread.joinable()) { clock_thread.join(); // Wait for the clock thread to finish } + RealSocket::log.logMessage(logger::LogLevel::INFO, "Clock stopped."); } } void GlobalClock::initializeSharedMemory() { - if (shared_clock == - nullptr) { // Check if shared memory is already initialized - shared_memory_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, - 0666); // Open shared memory file descriptor + if (shared_clock == nullptr) { // Check if shared memory is already initialized + shared_memory_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666); // Open shared memory file descriptor if (shared_memory_fd == -1) { - std::cerr << "Error accessing shared memory." << std::endl; + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Error accessing shared memory for the clock."); exit(1); // Exit if there's an error accessing shared memory } - ftruncate(shared_memory_fd, - sizeof(SharedClock)); // Set size of shared memory - shared_clock = static_cast( - mmap(0, sizeof(SharedClock), PROT_READ | PROT_WRITE, MAP_SHARED, - shared_memory_fd, 0)); // Map shared memory + ftruncate(shared_memory_fd, sizeof(SharedClock)); // Set size of shared memory + shared_clock = static_cast(mmap(0, sizeof(SharedClock), PROT_READ | PROT_WRITE, MAP_SHARED, shared_memory_fd, 0)); // Map shared memory if (shared_clock == MAP_FAILED) { - std::cerr << "Error mapping shared memory." << std::endl; + RealSocket::log.logMessage(logger::LogLevel::ERROR, "Error mapping shared memory for the clock."); exit(1); // Exit if there's an error mapping shared memory } + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Shared memory for the clock initialized."); registerCleanup(); // Register cleanup function to be called at exit } } @@ -76,44 +71,36 @@ void GlobalClock::initializeClockSynchronization() // Initialize the mutex attribute and set it to be shared between processes pthread_mutexattr_init(&mutex_attr); - pthread_mutexattr_setpshared( - &mutex_attr, - PTHREAD_PROCESS_SHARED); // Set mutex to be shared between processes - pthread_mutex_init(&shared_clock->tick_mutex, - &mutex_attr); // Initialize mutex in shared memory + pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); // Set mutex to be shared between processes + pthread_mutex_init(&shared_clock->tick_mutex, &mutex_attr); // Initialize mutex in shared memory // Initialize the condition attribute and set it to be shared between processes pthread_condattr_init(&cond_attr); - pthread_condattr_setpshared( - &cond_attr, - PTHREAD_PROCESS_SHARED); // Set condition variable to be shared between processes - pthread_cond_init( - &shared_clock->tick_cond, - &cond_attr); // Initialize condition variable in shared memory + pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); // Set condition variable to be shared between processes + pthread_cond_init(&shared_clock->tick_cond, &cond_attr); // Initialize condition variable in shared memory + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Clock synchronization initialized."); } void GlobalClock::releaseProcessResources() { - std::cout << "Releasing process-specific resources..." << std::endl; + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Releasing process-specific resources for the clock..."); if (shared_clock != nullptr) { - munmap( - shared_clock, - sizeof( - SharedClock)); // Unmap the shared memory from the process address space + munmap(shared_clock, sizeof(SharedClock)); // Unmap the shared memory from the process address space close(shared_memory_fd); // Close the file descriptor for shared memory shared_clock = nullptr; // Reset pointer to shared clock shared_memory_fd = -1; // Reset shared memory file descriptor + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Process-specific resources for the clock released."); } } void GlobalClock::releaseSharedSystemResources() { - std::cout << "Releasing system-wide shared resources..." << std::endl; + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Releasing system-wide shared resources for the clock..."); if (shared_memory_fd != -1) { - shm_unlink( - SHM_NAME); // Unlink shared memory from the system (delete it) + shm_unlink(SHM_NAME); // Unlink shared memory from the system (delete it) close(shared_memory_fd); // Close the file descriptor for shared memory shared_memory_fd = -1; // Reset shared memory file descriptor + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "System-wide shared resources for the clock released."); } } @@ -121,35 +108,30 @@ void GlobalClock::registerCleanup() { atexit([]() { // Register a cleanup function to be called when the process exits if (shared_clock->is_running.load()) { - GlobalClock:: - releaseProcessResources(); // Release process-specific resources if the clock is running + GlobalClock::releaseProcessResources(); // Release process-specific resources if the clock is running } else { - GlobalClock:: - releaseSharedSystemResources(); // Release system-wide resources if the clock is not running + GlobalClock::releaseSharedSystemResources(); // Release system-wide resources if the clock is not running } - std::cout << "Cleanup registered." << std::endl; }); } void GlobalClock::runClock() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Clock thread started."); while (shared_clock->is_running.load()) { std::this_thread::sleep_for(TICK_DURATION); // Sleep for tick duration - pthread_mutex_lock( - &shared_clock - ->tick_mutex); // Lock the mutex to safely increment the tick + pthread_mutex_lock(&shared_clock->tick_mutex); // Lock the mutex to safely increment the tick // Increment the tick and check for overflow if (shared_clock->current_tick.load() >= MAX_TICK) { shared_clock->current_tick.store(0); // Reset to 0 if max is reached + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Tick overflow, resetting to 0."); } else { shared_clock->current_tick++; // Increment the tick } - pthread_cond_broadcast( - &shared_clock - ->tick_cond); // Notify all waiting processes that the tick has changed - pthread_mutex_unlock( - &shared_clock->tick_mutex); // Unlock the mutex after tick change + pthread_cond_broadcast(&shared_clock->tick_cond); // Notify all waiting processes that the tick has changed + pthread_mutex_unlock(&shared_clock->tick_mutex); // Unlock the mutex after tick change } -} \ No newline at end of file + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Clock thread stopped."); +} diff --git a/communication/src/packet.cpp b/communication/src/packet.cpp index 7f4aa372..625c8d86 100644 --- a/communication/src/packet.cpp +++ b/communication/src/packet.cpp @@ -104,16 +104,15 @@ bool Packet::validateCRC() const return calculateCRC() == header.CRC; } -// A function to convert the data to hexa (logger) -std::string Packet::pointerToHex() const +// Static function to convert raw data to hexadecimal +std::string Packet::pointerToHex(const void* data, size_t size) { std::ostringstream oss; // Stream for constructing the hexadecimal output - const uint8_t* data = getPayload(); // Get the payload data - int dlc = getDLC(); // Get the data length code (DLC) + const uint8_t* byteData = static_cast(data); // Cast the void* to uint8_t* for byte-wise access - for (int i = 0; i < dlc; ++i) { - oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(data[i]); + for (size_t i = 0; i < size; ++i) { + oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(byteData[i]); } - return oss.str(); // Return the hex string representation of the payload + return oss.str(); // Return the hex string representation of the data } \ No newline at end of file diff --git a/communication/src/scheduler.cpp b/communication/src/scheduler.cpp index f070d950..a69ff0a4 100644 --- a/communication/src/scheduler.cpp +++ b/communication/src/scheduler.cpp @@ -2,26 +2,30 @@ Scheduler::Scheduler() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Scheduler created."); } Scheduler::~Scheduler() { stopAllTimers(); + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Scheduler destroyed and all timers stopped."); } void Scheduler::stopAllTimers() { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Stopping all timers."); for (auto &future : futures) { if (future.valid()) - { future.wait(); // Wait for all threads to finish - } } } void Scheduler::startRetransmissionTimer(int packetID, Callback callback, std::shared_ptr> ackPromise) { + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Starting retransmission timer for packet of message ID: " + + std::to_string(packetID)); + // Promise to manage the lifecycle of the thread itself std::promise threadCompletionPromise; @@ -35,9 +39,7 @@ void Scheduler::startRetransmissionTimer(int packetID, Callback callback, std::s std::thread([this, packetID, callback, threadCompletionPromise = std::move(threadCompletionPromise), ackPromise]() mutable { int retryCount = 0; - { - // Lock the mutex to synchronize access to shared data (ackReceived) std::unique_lock lock(mutex); // Wait for an ACK or timeout @@ -45,6 +47,9 @@ void Scheduler::startRetransmissionTimer(int packetID, Callback callback, std::s { return ackReceived[packetID]; })) { // ACK received within the timeout period + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "ACK received for packet of message ID: " + + std::to_string(packetID)); + clearPacketData(packetID); // Clear packet data // Set both promises to indicate success and thread completion @@ -57,6 +62,9 @@ void Scheduler::startRetransmissionTimer(int packetID, Callback callback, std::s // Timeout occurred, retransmit the packet retryCounts[packetID]++; retryCount = retryCounts[packetID]; + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Retransmitting packet of message ID: " + + std::to_string(packetID) + + ", retry count: " + std::to_string(retryCount)); } } @@ -73,6 +81,9 @@ void Scheduler::receiveACK(int packetID) { std::unique_lock lock(mutex); ackReceived[packetID] = true; + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "ACK received for packet of message ID: " + + std::to_string(packetID) + + ". Notifying all waiting threads."); cv.notify_all(); // Notify all waiting threads } @@ -80,4 +91,6 @@ void Scheduler::clearPacketData(int packetID) { ackReceived.erase(packetID); retryCounts.erase(packetID); -} \ No newline at end of file + RealSocket::log.logMessage(logger::LogLevel::DEBUG, "Cleared data for packet of message ID: " + + std::to_string(packetID)); +}