diff --git a/osal/common/CMakeLists.txt b/osal/common/CMakeLists.txt index b77985fb..af76484d 100644 --- a/osal/common/CMakeLists.txt +++ b/osal/common/CMakeLists.txt @@ -1,3 +1,16 @@ -target_sources(${LIB_FSFW_NAME} PRIVATE - tcpipCommon.cpp -) +if(DEFINED WIN32 OR DEFINED UNIX) + target_sources(${LIB_FSFW_NAME} PRIVATE + tcpipCommon.cpp + TcpIpBase.cpp + UdpTcPollingTask.cpp + UdpTmTcBridge.cpp + TcpTmTcServer.cpp + ) +endif() + +if(WIN32) + target_link_libraries(${LIB_FSFW_NAME} PRIVATE + wsock32 + ws2_32 + ) +endif() \ No newline at end of file diff --git a/osal/common/TcpIpBase.cpp b/osal/common/TcpIpBase.cpp new file mode 100644 index 00000000..27384ecc --- /dev/null +++ b/osal/common/TcpIpBase.cpp @@ -0,0 +1,54 @@ +#include "TcpIpBase.h" + +#ifdef __unix__ + +#include +#include + +#endif + +TcpIpBase::TcpIpBase() { + +} + +ReturnValue_t TcpIpBase::initialize() { +#ifdef _WIN32 + /* Initiates Winsock DLL. */ + WSAData wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + int err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + /* Tell the user that we could not find a usable Winsock DLL. */ +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: " << + err << std::endl; +#endif + return HasReturnvaluesIF::RETURN_FAILED; + } +#endif + return HasReturnvaluesIF::RETURN_OK; +} + +TcpIpBase::~TcpIpBase() { + closeSocket(serverSocket); +#ifdef _WIN32 + WSACleanup(); +#endif +} + +int TcpIpBase::closeSocket(socket_t socket) { +#ifdef _WIN32 + return closesocket(socket); +#elif defined(__unix__) + return close(socket); +#endif +} + +int TcpIpBase::getLastSocketError() { +#ifdef _WIN32 + return WSAGetLastError(); +#elif defined(__unix__) + return errno; +#endif +} + diff --git a/osal/common/TcpIpBase.h b/osal/common/TcpIpBase.h new file mode 100644 index 00000000..652d791a --- /dev/null +++ b/osal/common/TcpIpBase.h @@ -0,0 +1,50 @@ +#ifndef FSFW_OSAL_COMMON_TCPIPIF_H_ +#define FSFW_OSAL_COMMON_TCPIPIF_H_ + +#include + +#ifdef _WIN32 + +#include + +#elif defined(__unix__) + +#include + +#endif + +class TcpIpBase { +protected: + +#ifdef _WIN32 + static constexpr int SHUT_RECV = SD_RECEIVE; + static constexpr int SHUT_SEND = SD_SEND; + static constexpr int SHUT_BOTH = SD_BOTH; + + using socket_t = SOCKET; +#elif defined(__unix__) + using socket_t = int; + + static constexpr int INVALID_SOCKET = -1; + static constexpr int SOCKET_ERROR = -1; + + static constexpr int SHUT_RECV = SHUT_RD; + static constexpr int SHUT_SEND = SHUT_WR; + static constexpr int SHUT_BOTH = SHUT_RDWR; +#endif + + TcpIpBase(); + virtual ~TcpIpBase(); + + ReturnValue_t initialize(); + + int closeSocket(socket_t socket); + + int getLastSocketError(); + + socket_t serverSocket = 0; + +}; + + +#endif /* FSFW_OSAL_COMMON_TCPIPIF_H_ */ diff --git a/osal/windows/TcWinTcpServer.cpp b/osal/common/TcpTmTcServer.cpp similarity index 64% rename from osal/windows/TcWinTcpServer.cpp rename to osal/common/TcpTmTcServer.cpp index f68edfba..2da5da95 100644 --- a/osal/windows/TcWinTcpServer.cpp +++ b/osal/common/TcpTmTcServer.cpp @@ -1,14 +1,21 @@ -#include "TcWinTcpServer.h" +#include "TcpTmTcServer.h" #include "tcpipHelpers.h" #include "../../serviceinterface/ServiceInterface.h" +#ifdef _WIN32 #include #include -const std::string TcWinTcpServer::DEFAULT_TCP_SERVER_PORT = "7301"; -const std::string TcWinTcpServer::DEFAULT_TCP_CLIENT_PORT = "7302"; +#elif defined(__unix__) -TcWinTcpServer::TcWinTcpServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge, +#include + +#endif + +const std::string TcpTmTcServer::DEFAULT_TCP_SERVER_PORT = "7301"; +const std::string TcpTmTcServer::DEFAULT_TCP_CLIENT_PORT = "7302"; + +TcpTmTcServer::TcpTmTcServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge, std::string customTcpServerPort): SystemObject(objectId), tcpPort(customTcpServerPort) { if(tcpPort == "") { @@ -16,25 +23,18 @@ TcWinTcpServer::TcWinTcpServer(object_id_t objectId, object_id_t tmtcUnixUdpBrid } } -ReturnValue_t TcWinTcpServer::initialize() { +ReturnValue_t TcpTmTcServer::initialize() { using namespace tcpip; - int retval = 0; - struct addrinfo *addrResult = nullptr; - struct addrinfo hints; - /* Initiates Winsock DLL. */ - WSAData wsaData; - WORD wVersionRequested = MAKEWORD(2, 2); - int err = WSAStartup(wVersionRequested, &wsaData); - if (err != 0) { - /* Tell the user that we could not find a usable Winsock DLL. */ -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: " << - err << std::endl; -#endif - return HasReturnvaluesIF::RETURN_FAILED; + + ReturnValue_t result = TcpIpBase::initialize(); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; } - ZeroMemory(&hints, sizeof (hints)); + int retval = 0; + struct addrinfo *addrResult = nullptr; + struct addrinfo hints = { 0 }; + hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; @@ -43,7 +43,7 @@ ReturnValue_t TcWinTcpServer::initialize() { retval = getaddrinfo(nullptr, tcpPort.c_str(), &hints, &addrResult); if (retval != 0) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TcWinTcpServer::TcWinTcpServer: Retrieving address info failed!" << + sif::warning << "TcWinTcpServer::TcpTmTcServer: Retrieving address info failed!" << std::endl; #endif handleError(Protocol::TCP, ErrorSources::GETADDRINFO_CALL); @@ -65,7 +65,7 @@ ReturnValue_t TcWinTcpServer::initialize() { retval = bind(listenerTcpSocket, addrResult->ai_addr, static_cast(addrResult->ai_addrlen)); if(retval == SOCKET_ERROR) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TcWinTcpServer::TcWinTcpServer: Binding socket failed!" << + sif::warning << "TcWinTcpServer::TcpTmTcServer: Binding socket failed!" << std::endl; #endif freeaddrinfo(addrResult); @@ -77,17 +77,16 @@ ReturnValue_t TcWinTcpServer::initialize() { } -TcWinTcpServer::~TcWinTcpServer() { - closesocket(listenerTcpSocket); - WSACleanup(); +TcpTmTcServer::~TcpTmTcServer() { + closeSocket(listenerTcpSocket); } -ReturnValue_t TcWinTcpServer::performOperation(uint8_t opCode) { +ReturnValue_t TcpTmTcServer::performOperation(uint8_t opCode) { using namespace tcpip; /* If a connection is accepted, the corresponding socket will be assigned to the new socket */ - SOCKET clientSocket; - sockaddr_in clientSockAddr; - int connectorSockAddrLen = 0; + socket_t clientSocket; + sockaddr clientSockAddr; + socklen_t connectorSockAddrLen = 0; int retval = 0; /* Listen for connection requests permanently for lifetime of program */ @@ -98,8 +97,7 @@ ReturnValue_t TcWinTcpServer::performOperation(uint8_t opCode) { continue; } - clientSocket = accept(listenerTcpSocket, reinterpret_cast(&clientSockAddr), - &connectorSockAddrLen); + clientSocket = accept(listenerTcpSocket, &clientSockAddr, &connectorSockAddrLen); if(clientSocket == INVALID_SOCKET) { handleError(Protocol::TCP, ErrorSources::ACCEPT_CALL, 500); @@ -110,7 +108,7 @@ ReturnValue_t TcWinTcpServer::performOperation(uint8_t opCode) { receptionBuffer.size(), 0); if(retval > 0) { #if FSFW_TCP_RCV_WIRETAPPING_ENABLED == 1 - sif::info << "TcWinTcpServer::performOperation: Received " << retval << " bytes." + sif::info << "TcpTmTcServer::performOperation: Received " << retval << " bytes." std::endl; #endif handleError(Protocol::TCP, ErrorSources::RECV_CALL, 500); @@ -123,7 +121,7 @@ ReturnValue_t TcWinTcpServer::performOperation(uint8_t opCode) { } /* Done, shut down connection */ - retval = shutdown(clientSocket, SD_SEND); + retval = shutdown(clientSocket, SHUT_SEND); } return HasReturnvaluesIF::RETURN_OK; } diff --git a/osal/windows/TcWinTcpServer.h b/osal/common/TcpTmTcServer.h similarity index 79% rename from osal/windows/TcWinTcpServer.h rename to osal/common/TcpTmTcServer.h index bd9f3576..4dcc77a2 100644 --- a/osal/windows/TcWinTcpServer.h +++ b/osal/common/TcpTmTcServer.h @@ -1,9 +1,14 @@ #ifndef FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_ #define FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_ +#include "TcpIpBase.h" #include "../../objectmanager/SystemObject.h" #include "../../tasks/ExecutableObjectIF.h" +#ifdef __unix__ +#include +#endif + #include #include @@ -15,17 +20,18 @@ * @details * Based on: https://docs.microsoft.com/en-us/windows/win32/winsock/complete-server-code */ -class TcWinTcpServer: +class TcpTmTcServer: public SystemObject, + public TcpIpBase, public ExecutableObjectIF { public: /* The ports chosen here should not be used by any other process. */ static const std::string DEFAULT_TCP_SERVER_PORT; static const std::string DEFAULT_TCP_CLIENT_PORT; - TcWinTcpServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge, + TcpTmTcServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge, std::string customTcpServerPort = ""); - virtual~ TcWinTcpServer(); + virtual~ TcpTmTcServer(); ReturnValue_t initialize() override; ReturnValue_t performOperation(uint8_t opCode) override; @@ -33,8 +39,8 @@ public: private: std::string tcpPort; - SOCKET listenerTcpSocket = 0; - struct sockaddr_in tcpAddress; + socket_t listenerTcpSocket = 0; + struct sockaddr tcpAddress; int tcpAddrLen = sizeof(tcpAddress); int currentBacklog = 3; diff --git a/osal/common/UdpTcPollingTask.cpp b/osal/common/UdpTcPollingTask.cpp new file mode 100644 index 00000000..759cee05 --- /dev/null +++ b/osal/common/UdpTcPollingTask.cpp @@ -0,0 +1,177 @@ +#include "UdpTcPollingTask.h" +#include "tcpipHelpers.h" +#include "../../globalfunctions/arrayprinter.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" + +#ifdef _WIN32 + +#include + +#else + +#include +#include + +#endif + +//! Debugging preprocessor define. +#define FSFW_UDP_RCV_WIRETAPPING_ENABLED 0 + +UdpTcPollingTask::UdpTcPollingTask(object_id_t objectId, + object_id_t tmtcUnixUdpBridge, size_t maxRecvSize, + double timeoutSeconds): SystemObject(objectId), + tmtcBridgeId(tmtcUnixUdpBridge) { + if(frameSize > 0) { + this->frameSize = frameSize; + } + else { + this->frameSize = DEFAULT_MAX_RECV_SIZE; + } + + /* Set up reception buffer with specified frame size. + For now, it is assumed that only one frame is held in the buffer! */ + receptionBuffer.reserve(this->frameSize); + receptionBuffer.resize(this->frameSize); + + if(timeoutSeconds == -1) { + receptionTimeout = DEFAULT_TIMEOUT; + } + else { + receptionTimeout = timevalOperations::toTimeval(timeoutSeconds); + } +} + +UdpTcPollingTask::~UdpTcPollingTask() {} + +ReturnValue_t UdpTcPollingTask::performOperation(uint8_t opCode) { + /* Sender Address is cached here. */ + struct sockaddr senderAddress; + socklen_t senderAddressSize = sizeof(senderAddress); + + /* Poll for new UDP datagrams in permanent loop. */ + while(true) { + int bytesReceived = recvfrom( + this->serverSocket, + reinterpret_cast(receptionBuffer.data()), + frameSize, + receptionFlags, + &senderAddress, + &senderAddressSize + ); + if(bytesReceived == SOCKET_ERROR) { + /* Handle error */ +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "UdpTcPollingTask::performOperation: Reception error." << std::endl; +#endif + tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::RECVFROM_CALL, 1000); + continue; + } +#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1 + sif::debug << "UdpTcPollingTask::performOperation: " << bytesReceived << + " bytes received" << std::endl; +#endif + + ReturnValue_t result = handleSuccessfullTcRead(bytesReceived); + if(result != HasReturnvaluesIF::RETURN_FAILED) { + + } + tmtcBridge->checkAndSetClientAddress(senderAddress); + } + return HasReturnvaluesIF::RETURN_OK; +} + + +ReturnValue_t UdpTcPollingTask::handleSuccessfullTcRead(size_t bytesRead) { + store_address_t storeId; + +#if FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1 + arrayprinter::print(receptionBuffer.data(), bytesRead); +#endif + + ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRead); + if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning<< "UdpTcPollingTask::transferPusToSoftwareBus: Data storage failed." << + std::endl; + sif::warning << "Packet size: " << bytesRead << std::endl; +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ + return HasReturnvaluesIF::RETURN_FAILED; + } + + TmTcMessage message(storeId); + + result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message); + if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "UdpTcPollingTask::handleSuccessfullTcRead: " + " Sending message to queue failed" << std::endl; +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ + tcStore->deleteData(storeId); + } + return result; +} + +ReturnValue_t UdpTcPollingTask::initialize() { + tcStore = objectManager->get(objects::TC_STORE); + if (tcStore == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "UdpTcPollingTask::initialize: TC store uninitialized!" << std::endl; +#endif + return ObjectManagerIF::CHILD_INIT_FAILED; + } + + tmtcBridge = objectManager->get(tmtcBridgeId); + if(tmtcBridge == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "UdpTcPollingTask::initialize: Invalid TMTC bridge object!" << + std::endl; +#endif + return ObjectManagerIF::CHILD_INIT_FAILED; + } + + ReturnValue_t result = TcpIpBase::initialize(); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t UdpTcPollingTask::initializeAfterTaskCreation() { + /* Initialize the destination after task creation. This ensures + that the destination has already been set in the TMTC bridge. */ + targetTcDestination = tmtcBridge->getRequestQueue(); + /* The server socket is set up in the bridge intialization. Calling this function here + ensures that it is set up regardless of which class was initialized first */ + this->serverSocket = tmtcBridge->serverSocket; + return HasReturnvaluesIF::RETURN_OK; +} + +void UdpTcPollingTask::setTimeout(double timeoutSeconds) { +#ifdef _WIN32 + DWORD timeoutMs = timeoutSeconds * 1000.0; + int result = setsockopt(serverSocket, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&timeoutMs), sizeof(DWORD)); + if(result == -1) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TcWinUdpPollingTask::TcSocketPollingTask: Setting " + "receive timeout failed with " << strerror(errno) << std::endl; +#endif + } +#elif defined(__unix__) + timeval tval; + tval = timevalOperations::toTimeval(timeoutSeconds); + int result = setsockopt(serverSocket, SOL_SOCKET, SO_RCVTIMEO, + &tval, sizeof(receptionTimeout)); + if(result == -1) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TcSocketPollingTask::TcSocketPollingTask: Setting " + "receive timeout failed with " << strerror(errno) << std::endl; +#endif + } +#endif +} diff --git a/osal/windows/TcWinUdpPollingTask.h b/osal/common/UdpTcPollingTask.h similarity index 78% rename from osal/windows/TcWinUdpPollingTask.h rename to osal/common/UdpTcPollingTask.h index 35e3a701..ae1c4c44 100644 --- a/osal/windows/TcWinUdpPollingTask.h +++ b/osal/common/UdpTcPollingTask.h @@ -1,7 +1,7 @@ #ifndef FSFW_OSAL_WINDOWS_TCSOCKETPOLLINGTASK_H_ #define FSFW_OSAL_WINDOWS_TCSOCKETPOLLINGTASK_H_ -#include "TmTcWinUdpBridge.h" +#include "UdpTmTcBridge.h" #include "../../objectmanager/SystemObject.h" #include "../../tasks/ExecutableObjectIF.h" #include "../../storagemanager/StorageManagerIF.h" @@ -17,17 +17,19 @@ * This class caches the IP address of the sender. It is assumed there * is only one sender for now. */ -class TcWinUdpPollingTask: public SystemObject, +class UdpTcPollingTask: + public TcpIpBase, + public SystemObject, public ExecutableObjectIF { friend class TmTcWinUdpBridge; public: - static constexpr size_t DEFAULT_MAX_FRAME_SIZE = 2048; + static constexpr size_t DEFAULT_MAX_RECV_SIZE = 1500; //! 0.5 default milliseconds timeout for now. static constexpr timeval DEFAULT_TIMEOUT = {0, 500}; - TcWinUdpPollingTask(object_id_t objectId, object_id_t tmtcUnixUdpBridge, - size_t frameSize = 0, double timeoutSeconds = -1); - virtual~ TcWinUdpPollingTask(); + UdpTcPollingTask(object_id_t objectId, object_id_t tmtcUnixUdpBridge, + size_t maxRecvSize = 0, double timeoutSeconds = -1); + virtual~ UdpTcPollingTask(); /** * Turn on optional timeout for UDP polling. In the default mode, @@ -46,16 +48,12 @@ protected: private: //! TMTC bridge is cached. object_id_t tmtcBridgeId = objects::NO_OBJECT; - TmTcWinUdpBridge* tmtcBridge = nullptr; + UdpTmTcBridge* tmtcBridge = nullptr; MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE; //! See: https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom int receptionFlags = 0; - //! Server socket, which is member of TMTC bridge. - //! Will be cached shortly after SW intialization. - SOCKET serverUdpSocket = 0; - std::vector receptionBuffer; size_t frameSize = 0; diff --git a/osal/windows/TmTcWinUdpBridge.cpp b/osal/common/UdpTmTcBridge.cpp similarity index 72% rename from osal/windows/TmTcWinUdpBridge.cpp rename to osal/common/UdpTmTcBridge.cpp index 03ca52cd..7f3dc929 100644 --- a/osal/windows/TmTcWinUdpBridge.cpp +++ b/osal/common/UdpTmTcBridge.cpp @@ -1,17 +1,26 @@ -#include "TmTcWinUdpBridge.h" #include "tcpipHelpers.h" -#include "../../ipc/MutexGuard.h" -#include "../../serviceinterface/ServiceInterface.h" +#include +#include +#include + +#ifdef _WIN32 #include +#elif defined(__unix__) + +#include +#include + +#endif + //! Debugging preprocessor define. #define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0 -const std::string TmTcWinUdpBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_UDP_SERVER_PORT; +const std::string UdpTmTcBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT; -TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId, object_id_t tcDestination, +UdpTmTcBridge::UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination, object_id_t tmStoreId, object_id_t tcStoreId, std::string udpServerPort): TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) { if(udpServerPort == "") { @@ -25,16 +34,18 @@ TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId, object_id_t tcDestinati communicationLinkUp = false; } -ReturnValue_t TmTcWinUdpBridge::initialize() { +ReturnValue_t UdpTmTcBridge::initialize() { ReturnValue_t result = TmTcBridge::initialize(); if(result != HasReturnvaluesIF::RETURN_OK) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TmTcWinUdpBridge::initialize: TmTcBridge initialization failed!" + sif::error << "TmTcUdpBridge::initialize: TmTcBridge initialization failed!" << std::endl; #endif return result; } + +#ifdef _WIN32 /* Initiates Winsock DLL. */ WSAData wsaData; WORD wVersionRequested = MAKEWORD(2, 2); @@ -43,26 +54,22 @@ ReturnValue_t TmTcWinUdpBridge::initialize() { /* Tell the user that we could not find a usable */ /* Winsock DLL. */ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: " << + sif::error << "TmTcUdpBridge::TmTcUdpBridge: WSAStartup failed with error: " << err << std::endl; #else - sif::printError("TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: %d\n", + sif::printError("TmTcUdpBridge::TmTcUdpBridge: WSAStartup failed with error: %d\n", err); #endif return HasReturnvaluesIF::RETURN_FAILED; } +#endif struct addrinfo *addrResult = nullptr; - struct addrinfo hints; + struct addrinfo hints = {}; - ZeroMemory(&hints, sizeof (hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; - /* See: - https://docs.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo - for information about AI_PASSIVE. */ - hints.ai_flags = AI_PASSIVE; /* Set up UDP socket: https://en.wikipedia.org/wiki/Getaddrinfo @@ -71,7 +78,7 @@ ReturnValue_t TmTcWinUdpBridge::initialize() { int retval = getaddrinfo(nullptr, udpServerPort.c_str(), &hints, &addrResult); if (retval != 0) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Retrieving address info failed!" << + sif::warning << "TmTcUdpBridge::TmTcUdpBridge: Retrieving address info failed!" << std::endl; #endif return HasReturnvaluesIF::RETURN_FAILED; @@ -80,7 +87,7 @@ ReturnValue_t TmTcWinUdpBridge::initialize() { serverSocket = socket(addrResult->ai_family, addrResult->ai_socktype, addrResult->ai_protocol); if(serverSocket == INVALID_SOCKET) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not open UDP socket!" << + sif::warning << "TmTcUdpBridge::TmTcUdpBridge: Could not open UDP socket!" << std::endl; #endif freeaddrinfo(addrResult); @@ -91,7 +98,7 @@ ReturnValue_t TmTcWinUdpBridge::initialize() { retval = bind(serverSocket, addrResult->ai_addr, static_cast(addrResult->ai_addrlen)); if(retval != 0) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not bind " + sif::error << "TmTcUdpBridge::TmTcUdpBridge: Could not bind " "local port (" << udpServerPort << ") to server socket!" << std::endl; #endif freeaddrinfo(addrResult); @@ -101,15 +108,13 @@ ReturnValue_t TmTcWinUdpBridge::initialize() { return HasReturnvaluesIF::RETURN_OK; } -TmTcWinUdpBridge::~TmTcWinUdpBridge() { +UdpTmTcBridge::~UdpTmTcBridge() { if(mutex != nullptr) { MutexFactory::instance()->deleteMutex(mutex); } - closesocket(serverSocket); - WSACleanup(); } -ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { +ReturnValue_t UdpTmTcBridge::sendTm(const uint8_t *data, size_t dataLen) { int flags = 0; /* The target address can be set by different threads so this lock ensures thread-safety */ @@ -126,23 +131,23 @@ ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { reinterpret_cast(data), dataLen, flags, - reinterpret_cast(&clientAddress), + &clientAddress, clientAddressLen ); if(bytesSent == SOCKET_ERROR) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TmTcWinUdpBridge::sendTm: Send operation failed." << std::endl; + sif::warning << "TmTcUdpBridge::sendTm: Send operation failed." << std::endl; #endif tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::SENDTO_CALL); } #if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1 - sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were" + sif::debug << "TmTcUdpBridge::sendTm: " << bytesSent << " bytes were" " sent." << std::endl; #endif return HasReturnvaluesIF::RETURN_OK; } -void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in& newAddress) { +void UdpTmTcBridge::checkAndSetClientAddress(sockaddr& newAddress) { /* The target address can be set by different threads so this lock ensures thread-safety */ MutexGuard lock(mutex, timeoutType, mutexTimeoutMs); @@ -160,7 +165,7 @@ void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in& newAddress) { clientAddressLen = sizeof(clientAddress); } -void TmTcWinUdpBridge::setMutexProperties(MutexIF::TimeoutType timeoutType, +void UdpTmTcBridge::setMutexProperties(MutexIF::TimeoutType timeoutType, dur_millis_t timeoutMs) { this->timeoutType = timeoutType; this->mutexTimeoutMs = timeoutMs; diff --git a/osal/windows/TmTcWinUdpBridge.h b/osal/common/UdpTmTcBridge.h similarity index 68% rename from osal/windows/TmTcWinUdpBridge.h rename to osal/common/UdpTmTcBridge.h index c2f7d6aa..6f38034e 100644 --- a/osal/windows/TmTcWinUdpBridge.h +++ b/osal/common/UdpTmTcBridge.h @@ -1,20 +1,32 @@ #ifndef FSFW_OSAL_WINDOWS_TMTCWINUDPBRIDGE_H_ #define FSFW_OSAL_WINDOWS_TMTCWINUDPBRIDGE_H_ +#include "TcpIpBase.h" #include "../../tmtcservices/TmTcBridge.h" -#include -#include +#ifdef _WIN32 -class TmTcWinUdpBridge: public TmTcBridge { - friend class TcWinUdpPollingTask; +#include + +#elif defined(__unix__) + +#include + +#endif + +#include + +class UdpTmTcBridge: + public TmTcBridge, + public TcpIpBase { + friend class UdpTcPollingTask; public: /* The ports chosen here should not be used by any other process. */ static const std::string DEFAULT_UDP_SERVER_PORT; - TmTcWinUdpBridge(object_id_t objectId, object_id_t tcDestination, + UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination, object_id_t tmStoreId, object_id_t tcStoreId, std::string udpServerPort = ""); - virtual~ TmTcWinUdpBridge(); + virtual~ UdpTmTcBridge(); /** * Set properties of internal mutex. @@ -23,17 +35,16 @@ public: ReturnValue_t initialize() override; - void checkAndSetClientAddress(sockaddr_in& clientAddress); + void checkAndSetClientAddress(sockaddr& clientAddress); protected: virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override; private: - SOCKET serverSocket = 0; std::string udpServerPort; - struct sockaddr_in clientAddress; - int clientAddressLen = 0; + struct sockaddr clientAddress; + socklen_t clientAddressLen = 0; //! Access to the client address is mutex protected as it is set by another task. MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING; diff --git a/osal/common/tcpipCommon.h b/osal/common/tcpipCommon.h index 9b38c9fb..dc5ada52 100644 --- a/osal/common/tcpipCommon.h +++ b/osal/common/tcpipCommon.h @@ -6,8 +6,7 @@ namespace tcpip { -const char* const DEFAULT_UDP_SERVER_PORT = "7301"; -const char* const DEFAULT_TCP_SERVER_PORT = "7303"; +const char* const DEFAULT_SERVER_PORT = "7301"; enum class Protocol { UDP, diff --git a/osal/windows/tcpipHelpers.h b/osal/common/tcpipHelpers.h similarity index 89% rename from osal/windows/tcpipHelpers.h rename to osal/common/tcpipHelpers.h index 01f009b9..9764a93f 100644 --- a/osal/windows/tcpipHelpers.h +++ b/osal/common/tcpipHelpers.h @@ -2,7 +2,7 @@ #define FSFW_OSAL_WINDOWS_TCPIPHELPERS_H_ #include "../../timemanager/clockDefinitions.h" -#include "../common/tcpipCommon.h" +#include "tcpipCommon.h" namespace tcpip { diff --git a/osal/host/PeriodicTask.cpp b/osal/host/PeriodicTask.cpp index 7663d522..4c0bf338 100644 --- a/osal/host/PeriodicTask.cpp +++ b/osal/host/PeriodicTask.cpp @@ -10,6 +10,7 @@ #if defined(WIN32) #include +#include #elif defined(LINUX) #include #endif @@ -22,33 +23,11 @@ PeriodicTask::PeriodicTask(const char *name, TaskPriority setPriority, // It is propably possible to set task priorities by using the native // task handles for Windows / Linux mainThread = std::thread(&PeriodicTask::taskEntryPoint, this, this); -#if defined(WIN32) - /* List of possible priority classes: - * https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ - * nf-processthreadsapi-setpriorityclass - * And respective thread priority numbers: - * https://docs.microsoft.com/en-us/windows/ - * win32/procthread/scheduling-priorities */ - int result = SetPriorityClass( - reinterpret_cast(mainThread.native_handle()), - ABOVE_NORMAL_PRIORITY_CLASS); - if(result != 0) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PeriodicTask: Windows SetPriorityClass failed with code " - << GetLastError() << std::endl; -#endif - } - result = SetThreadPriority( - reinterpret_cast(mainThread.native_handle()), - THREAD_PRIORITY_NORMAL); - if(result != 0) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PeriodicTask: Windows SetPriorityClass failed with code " - << GetLastError() << std::endl; -#endif - } -#elif defined(LINUX) - // we can just copy and paste the code from linux here. +#if defined(_WIN32) + tasks::setTaskPriority(reinterpret_cast(mainThread.native_handle()), setPriority); + tasks::insertTaskName(mainThread.get_id(), taskName); +#elif defined(__unix__) + // TODO: We could reuse existing code here. #endif } diff --git a/osal/host/TaskFactory.cpp b/osal/host/TaskFactory.cpp index 4fafd349..4ac44364 100644 --- a/osal/host/TaskFactory.cpp +++ b/osal/host/TaskFactory.cpp @@ -1,14 +1,20 @@ +#include "../../tasks/TaskFactory.h" #include "../../osal/host/FixedTimeslotTask.h" #include "../../osal/host/PeriodicTask.h" -#include "../../tasks/TaskFactory.h" #include "../../returnvalues/HasReturnvaluesIF.h" #include "../../tasks/PeriodicTaskIF.h" +#ifdef _WIN32 + +#include "../windows/winTaskHelpers.h" + +#endif + #include TaskFactory* TaskFactory::factoryInstance = new TaskFactory(); -// Will propably not be used for hosted implementation +// Not used for the host implementation for now because C++ thread abstraction is used const size_t PeriodicTaskIF::MINIMUM_STACK_SIZE = 0; TaskFactory::TaskFactory() { @@ -49,8 +55,33 @@ ReturnValue_t TaskFactory::delayTask(uint32_t delayMs){ } void TaskFactory::printMissedDeadline() { - /* TODO: Implement */ - return; +#ifdef __unix__ + char name[20] = {0}; + int status = pthread_getname_np(pthread_self(), name, sizeof(name)); +#if FSFW_CPP_OSTREAM_ENABLED == 1 + if(status == 0) { + sif::warning << "TaskFactory::printMissedDeadline: " << name << "" << std::endl; + } + else { + sif::warning << "TaskFactory::printMissedDeadline: Unknown task name" << status << + std::endl; + } +#else + if(status == 0) { + sif::printWarning("TaskFactory::printMissedDeadline: %s\n", name); + } + else { + sif::printWarning("TaskFactory::printMissedDeadline: Unknown task name\n", name); + } +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#elif defined(_WIN32) + std::string name = tasks::getTaskName(std::this_thread::get_id()); +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "TaskFactory::printMissedDeadline: " << name << std::endl; +#else + sif::printWarning("TaskFactory::printMissedDeadline: %s\n", name); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* defined(_WIN32) */ } diff --git a/osal/linux/CMakeLists.txt b/osal/linux/CMakeLists.txt index 6c377844..332fe2f4 100644 --- a/osal/linux/CMakeLists.txt +++ b/osal/linux/CMakeLists.txt @@ -13,8 +13,6 @@ target_sources(${LIB_FSFW_NAME} QueueFactory.cpp SemaphoreFactory.cpp TaskFactory.cpp - TcUnixUdpPollingTask.cpp - TmTcUnixUdpBridge.cpp Timer.cpp tcpipHelpers.cpp ) diff --git a/osal/linux/PosixThread.cpp b/osal/linux/PosixThread.cpp index bd8e7258..5296dfb5 100644 --- a/osal/linux/PosixThread.cpp +++ b/osal/linux/PosixThread.cpp @@ -6,7 +6,7 @@ #include PosixThread::PosixThread(const char* name_, int priority_, size_t stackSize_): - thread(0),priority(priority_),stackSize(stackSize_) { + thread(0), priority(priority_), stackSize(stackSize_) { name[0] = '\0'; std::strncat(name, name_, PTHREAD_MAX_NAMELEN - 1); } @@ -75,18 +75,18 @@ bool PosixThread::delayUntil(uint64_t* const prevoiusWakeTime_ms, if (currentTime_ms < *prevoiusWakeTime_ms) { /* The tick count has overflowed since this function was - lasted called. In this case the only time we should ever - actually delay is if the wake time has also overflowed, - and the wake time is greater than the tick time. When this - is the case it is as if neither time had overflowed. */ + lasted called. In this case the only time we should ever + actually delay is if the wake time has also overflowed, + and the wake time is greater than the tick time. When this + is the case it is as if neither time had overflowed. */ if ((nextTimeToWake_ms < *prevoiusWakeTime_ms) && (nextTimeToWake_ms > currentTime_ms)) { shouldDelay = true; } } else { - /* The tick time has not overflowed. In this case we will - delay if either the wake time has overflowed, and/or the - tick time is less than the wake time. */ + /* The tick time has not overflowed. In this case we will + delay if either the wake time has overflowed, and/or the + tick time is less than the wake time. */ if ((nextTimeToWake_ms < *prevoiusWakeTime_ms) || (nextTimeToWake_ms > currentTime_ms)) { shouldDelay = true; diff --git a/osal/linux/TcUnixUdpPollingTask.cpp b/osal/linux/TcUnixUdpPollingTask.cpp deleted file mode 100644 index 37dadb76..00000000 --- a/osal/linux/TcUnixUdpPollingTask.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "TcUnixUdpPollingTask.h" -#include "tcpipHelpers.h" - -#include "../../globalfunctions/arrayprinter.h" - -#define FSFW_UDP_RCV_WIRETAPPING_ENABLED 0 - -TcUnixUdpPollingTask::TcUnixUdpPollingTask(object_id_t objectId, - object_id_t tmtcUnixUdpBridge, size_t frameSize, - double timeoutSeconds): SystemObject(objectId), - tmtcBridgeId(tmtcUnixUdpBridge) { - - if(frameSize > 0) { - this->frameSize = frameSize; - } - else { - this->frameSize = DEFAULT_MAX_FRAME_SIZE; - } - - /* Set up reception buffer with specified frame size. - For now, it is assumed that only one frame is held in the buffer! */ - receptionBuffer.reserve(this->frameSize); - receptionBuffer.resize(this->frameSize); - - if(timeoutSeconds == -1) { - receptionTimeout = DEFAULT_TIMEOUT; - } - else { - receptionTimeout = timevalOperations::toTimeval(timeoutSeconds); - } -} - -TcUnixUdpPollingTask::~TcUnixUdpPollingTask() {} - -ReturnValue_t TcUnixUdpPollingTask::performOperation(uint8_t opCode) { - /* Sender Address is cached here. */ - struct sockaddr_in senderAddress; - socklen_t senderAddressSize = sizeof(senderAddress); - - /* Poll for new UDP datagrams in permanent loop. */ - while(true) { - ssize_t bytesReceived = recvfrom( - serverUdpSocket, - receptionBuffer.data(), - frameSize, - receptionFlags, - reinterpret_cast(&senderAddress), - &senderAddressSize - ); - if(bytesReceived < 0) { - /* Handle error */ -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TcSocketPollingTask::performOperation: Reception error." << std::endl; -#endif - tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::RECVFROM_CALL, 500); - continue; - } -#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1 - sif::debug << "TcSocketPollingTask::performOperation: " << bytesReceived - << " bytes received" << std::endl; -#endif - - ReturnValue_t result = handleSuccessfullTcRead(bytesReceived); - if(result != HasReturnvaluesIF::RETURN_FAILED) { - - } - tmtcBridge->checkAndSetClientAddress(senderAddress); - } - return HasReturnvaluesIF::RETURN_OK; -} - - -ReturnValue_t TcUnixUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) { - store_address_t storeId; - -#if FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1 - arrayprinter::print(receptionBuffer.data(), bytesRead); -#endif - - ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRead); - if (result != HasReturnvaluesIF::RETURN_OK) { -#if FSFW_VERBOSE_LEVEL >= 1 -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TcUnixUdpPollingTask::handleSuccessfullTcRead: Data " - "storage failed" << std::endl; - sif::error << "Packet size: " << bytesRead << std::endl; -#else -#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ -#endif /* FSFW_VERBOSE_LEVEL >= 1 */ - return HasReturnvaluesIF::RETURN_FAILED; - } - - TmTcMessage message(storeId); - - result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message); - if (result != HasReturnvaluesIF::RETURN_OK) { -#if FSFW_VERBOSE_LEVEL >= 1 -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TcUnixUdpPollingTask::handleSuccessfullTcRead: Sending message to queue " - "failed" << std::endl; -#else -#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ -#endif /* FSFW_VERBOSE_LEVEL >= 1 */ - tcStore->deleteData(storeId); - } - return result; -} - -ReturnValue_t TcUnixUdpPollingTask::initialize() { - tcStore = objectManager->get(objects::TC_STORE); - if (tcStore == nullptr) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TcSerialPollingTask::initialize: TC Store uninitialized!" - << std::endl; -#endif - return ObjectManagerIF::CHILD_INIT_FAILED; - } - - tmtcBridge = objectManager->get(tmtcBridgeId); - if(tmtcBridge == nullptr) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TcSocketPollingTask::TcSocketPollingTask: Invalid" - " TMTC bridge object!" << std::endl; -#endif - return ObjectManagerIF::CHILD_INIT_FAILED; - } - - return HasReturnvaluesIF::RETURN_OK; -} - -ReturnValue_t TcUnixUdpPollingTask::initializeAfterTaskCreation() { - /* Initialize the destination after task creation. This ensures - that the destination has already been set in the TMTC bridge. */ - targetTcDestination = tmtcBridge->getRequestQueue(); - /* The server socket is set up in the bridge intialization. Calling this function here - ensures that it is set up properly in any case*/ - serverUdpSocket = tmtcBridge->serverSocket; - return HasReturnvaluesIF::RETURN_OK; -} - -void TcUnixUdpPollingTask::setTimeout(double timeoutSeconds) { - timeval tval; - tval = timevalOperations::toTimeval(timeoutSeconds); - int result = setsockopt(serverUdpSocket, SOL_SOCKET, SO_RCVTIMEO, - &tval, sizeof(receptionTimeout)); - if(result == -1) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TcSocketPollingTask::TcSocketPollingTask: Setting " - "receive timeout failed with " << strerror(errno) << std::endl; -#endif - } -} diff --git a/osal/linux/TcUnixUdpPollingTask.h b/osal/linux/TcUnixUdpPollingTask.h deleted file mode 100644 index 39ee0914..00000000 --- a/osal/linux/TcUnixUdpPollingTask.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ -#define FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ - -#include "../../objectmanager/SystemObject.h" -#include "../../osal/linux/TmTcUnixUdpBridge.h" -#include "../../tasks/ExecutableObjectIF.h" - -#include -#include - -/** - * @brief This class can be used to implement the polling of a Unix socket, - * using UDP for now. - * @details - * The task will be blocked while the specified number of bytes has not been - * received, so TC reception is handled inside a separate task. - * This class caches the IP address of the sender. It is assumed there - * is only one sender for now. - */ -class TcUnixUdpPollingTask: public SystemObject, - public ExecutableObjectIF { - friend class TmTcUnixUdpBridge; -public: - static constexpr size_t DEFAULT_MAX_FRAME_SIZE = 2048; - //! 0.5 default milliseconds timeout for now. - static constexpr timeval DEFAULT_TIMEOUT = {.tv_sec = 0, .tv_usec = 500}; - - TcUnixUdpPollingTask(object_id_t objectId, object_id_t tmtcUnixUdpBridge, - size_t frameSize = 0, double timeoutSeconds = -1); - virtual~ TcUnixUdpPollingTask(); - - /** - * Turn on optional timeout for UDP polling. In the default mode, - * the receive function will block until a packet is received. - * @param timeoutSeconds - */ - void setTimeout(double timeoutSeconds); - - virtual ReturnValue_t performOperation(uint8_t opCode) override; - virtual ReturnValue_t initialize() override; - virtual ReturnValue_t initializeAfterTaskCreation() override; - -protected: - StorageManagerIF* tcStore = nullptr; - -private: - //! TMTC bridge is cached. - object_id_t tmtcBridgeId = objects::NO_OBJECT; - TmTcUnixUdpBridge* tmtcBridge = nullptr; - MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE; - - //! Reception flags: https://linux.die.net/man/2/recvfrom. - int receptionFlags = 0; - - //! Server socket, which is member of TMTC bridge and is assigned in - //! constructor - int serverUdpSocket = 0; - - std::vector receptionBuffer; - - size_t frameSize = 0; - timeval receptionTimeout; - - ReturnValue_t handleSuccessfullTcRead(size_t bytesRead); -}; - -#endif /* FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ */ diff --git a/osal/linux/TmTcUnixUdpBridge.cpp b/osal/linux/TmTcUnixUdpBridge.cpp deleted file mode 100644 index fa7913ea..00000000 --- a/osal/linux/TmTcUnixUdpBridge.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "TmTcUnixUdpBridge.h" -#include "tcpipHelpers.h" -#include "../../serviceinterface/ServiceInterface.h" -#include "../../ipc/MutexGuard.h" - -#include -#include -#include - -#include - -//! Debugging preprocessor define. -#define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0 - -const std::string TmTcUnixUdpBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_UDP_SERVER_PORT; - -TmTcUnixUdpBridge::TmTcUnixUdpBridge(object_id_t objectId, object_id_t tcDestination, - object_id_t tmStoreId, object_id_t tcStoreId, std::string udpServerPort): - TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) { - if(udpServerPort == "") { - this->udpServerPort = DEFAULT_UDP_SERVER_PORT; - } - else { - this->udpServerPort = udpServerPort; - } - - mutex = MutexFactory::instance()->createMutex(); - communicationLinkUp = false; -} - -ReturnValue_t TmTcUnixUdpBridge::initialize() { - using namespace tcpip; - - ReturnValue_t result = TmTcBridge::initialize(); - if(result != HasReturnvaluesIF::RETURN_OK) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TmTcUnixUdpBridge::initialize: TmTcBridge initialization failed!" - << std::endl; -#endif - return result; - } - - struct addrinfo *addrResult = nullptr; - struct addrinfo hints; - - std::memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - hints.ai_flags = AI_PASSIVE; - - /* Set up UDP socket: - https://man7.org/linux/man-pages/man3/getaddrinfo.3.html - Passing nullptr as the first parameter and specifying AI_PASSIVE in hints will cause - getaddrinfo to assign the address 0.0.0.0 (any address) */ - int retval = getaddrinfo(nullptr, udpServerPort.c_str(), &hints, &addrResult); - if (retval != 0) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Retrieving address info failed!" << - std::endl; -#endif - return HasReturnvaluesIF::RETURN_FAILED; - } - - /* Set up UDP socket: https://man7.org/linux/man-pages/man7/ip.7.html */ - serverSocket = socket(addrResult->ai_family, addrResult->ai_socktype, addrResult->ai_protocol); - if(serverSocket < 0) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not open UDP socket!" << - std::endl; -#else - sif::printError("TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not open UDP socket!\n"); -#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ - freeaddrinfo(addrResult); - handleError(Protocol::UDP, ErrorSources::SOCKET_CALL); - return HasReturnvaluesIF::RETURN_FAILED; - } - - retval = bind(serverSocket, addrResult->ai_addr, static_cast(addrResult->ai_addrlen)); - if(retval != 0) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not bind " - "local port (" << udpServerPort << ") to server socket!" << std::endl; -#endif - freeaddrinfo(addrResult); - handleError(Protocol::UDP, ErrorSources::BIND_CALL); - return HasReturnvaluesIF::RETURN_FAILED; - } - - return HasReturnvaluesIF::RETURN_OK; -} - -TmTcUnixUdpBridge::~TmTcUnixUdpBridge() { - if(mutex != nullptr) { - MutexFactory::instance()->deleteMutex(mutex); - } - close(serverSocket); -} - -ReturnValue_t TmTcUnixUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { - int flags = 0; - - /* The target address can be set by different threads so this lock ensures thread-safety */ - MutexGuard lock(mutex, timeoutType, mutexTimeoutMs); - - if(ipAddrAnySet){ - clientAddress.sin_addr.s_addr = htons(INADDR_ANY); - clientAddressLen = sizeof(clientAddress); - } - -#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1 - char ipAddress [15]; - sif::debug << "IP Address Sender: "<< - inet_ntop(AF_INET,&clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; -#endif - - ssize_t bytesSent = sendto( - serverSocket, - data, - dataLen, - flags, - reinterpret_cast(&clientAddress), - clientAddressLen - ); - if(bytesSent < 0) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TmTcUnixUdpBridge::sendTm: Send operation failed." << std::endl; -#endif - tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::SENDTO_CALL); - } - -#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1 - sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were" - " sent." << std::endl; -#endif - - return HasReturnvaluesIF::RETURN_OK; -} - -void TmTcUnixUdpBridge::checkAndSetClientAddress(sockaddr_in& newAddress) { - /* The target address can be set by different threads so this lock ensures thread-safety */ - MutexGuard lock(mutex, timeoutType, mutexTimeoutMs); - -#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1 - char ipAddress [15]; - sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, - &newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; - sif::debug << "IP Address Old: " << inet_ntop(AF_INET, - &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; -#endif - registerCommConnect(); - - /* Set new IP address to reply to. */ - clientAddress = newAddress; - clientAddressLen = sizeof(clientAddress); -} - -void TmTcUnixUdpBridge::setMutexProperties(MutexIF::TimeoutType timeoutType, - dur_millis_t timeoutMs) { - this->timeoutType = timeoutType; - this->mutexTimeoutMs = timeoutMs; -} - -void TmTcUnixUdpBridge::setClientAddressToAny(bool ipAddrAnySet){ - this->ipAddrAnySet = ipAddrAnySet; -} - diff --git a/osal/linux/TmTcUnixUdpBridge.h b/osal/linux/TmTcUnixUdpBridge.h deleted file mode 100644 index 3ab2118c..00000000 --- a/osal/linux/TmTcUnixUdpBridge.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef FRAMEWORK_OSAL_LINUX_TMTCUNIXUDPBRIDGE_H_ -#define FRAMEWORK_OSAL_LINUX_TMTCUNIXUDPBRIDGE_H_ - -#include "../../tmtcservices/AcceptsTelecommandsIF.h" -#include "../../tmtcservices/TmTcBridge.h" -#include -#include -#include - -class TmTcUnixUdpBridge: - public TmTcBridge { - friend class TcUnixUdpPollingTask; -public: - - /* The ports chosen here should not be used by any other process. - List of used ports on Linux: /etc/services */ - static const std::string DEFAULT_UDP_SERVER_PORT; - - TmTcUnixUdpBridge(object_id_t objectId, object_id_t tcDestination, - object_id_t tmStoreId, object_id_t tcStoreId, - std::string serverPort = ""); - virtual~ TmTcUnixUdpBridge(); - - /** - * Set properties of internal mutex. - */ - void setMutexProperties(MutexIF::TimeoutType timeoutType, dur_millis_t timeoutMs); - - ReturnValue_t initialize() override; - - void checkAndSetClientAddress(sockaddr_in& clientAddress); - - void setClientAddressToAny(bool ipAddrAnySet); - -protected: - virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override; - -private: - int serverSocket = 0; - std::string udpServerPort; - - struct sockaddr_in clientAddress; - socklen_t clientAddressLen = 0; - - bool ipAddrAnySet = false; - - //! Access to the client address is mutex protected as it is set by another task. - MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING; - dur_millis_t mutexTimeoutMs = 20; - MutexIF* mutex; -}; - -#endif /* FRAMEWORK_OSAL_LINUX_TMTCUNIXUDPBRIDGE_H_ */ diff --git a/osal/linux/tcpipHelpers.cpp b/osal/linux/tcpipHelpers.cpp index 1c380769..4c1b9a78 100644 --- a/osal/linux/tcpipHelpers.cpp +++ b/osal/linux/tcpipHelpers.cpp @@ -1,4 +1,4 @@ -#include "tcpipHelpers.h" +#include "../common/tcpipHelpers.h" #include "../../tasks/TaskFactory.h" diff --git a/osal/linux/tcpipHelpers.h b/osal/linux/tcpipHelpers.h deleted file mode 100644 index 6b337c62..00000000 --- a/osal/linux/tcpipHelpers.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef FSFW_OSAL_LINUX_TCPIPHELPERS_H_ -#define FSFW_OSAL_LINUX_TCPIPHELPERS_H_ - -#include "../../timemanager/clockDefinitions.h" -#include "../common/tcpipCommon.h" - -namespace tcpip { - - -void handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t sleepDuration = 0); - -} - -#endif /* FSFW_OSAL_LINUX_TCPIPHELPERS_H_ */ diff --git a/osal/windows/CMakeLists.txt b/osal/windows/CMakeLists.txt index a2b31688..36a54765 100644 --- a/osal/windows/CMakeLists.txt +++ b/osal/windows/CMakeLists.txt @@ -1,11 +1,4 @@ target_sources(${LIB_FSFW_NAME} PRIVATE - TcWinUdpPollingTask.cpp - TmTcWinUdpBridge.cpp - TcWinTcpServer.cpp tcpipHelpers.cpp + winTaskHelpers.cpp ) - -target_link_libraries(${LIB_FSFW_NAME} PRIVATE - wsock32 - ws2_32 -) \ No newline at end of file diff --git a/osal/windows/TcWinUdpPollingTask.cpp b/osal/windows/TcWinUdpPollingTask.cpp deleted file mode 100644 index 980404f9..00000000 --- a/osal/windows/TcWinUdpPollingTask.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "TcWinUdpPollingTask.h" -#include "tcpipHelpers.h" -#include "../../globalfunctions/arrayprinter.h" -#include "../../serviceinterface/ServiceInterfaceStream.h" - -#include - -//! Debugging preprocessor define. -#define FSFW_UDP_RCV_WIRETAPPING_ENABLED 0 - -TcWinUdpPollingTask::TcWinUdpPollingTask(object_id_t objectId, - object_id_t tmtcUnixUdpBridge, size_t frameSize, - double timeoutSeconds): SystemObject(objectId), - tmtcBridgeId(tmtcUnixUdpBridge) { - if(frameSize > 0) { - this->frameSize = frameSize; - } - else { - this->frameSize = DEFAULT_MAX_FRAME_SIZE; - } - - /* Set up reception buffer with specified frame size. - For now, it is assumed that only one frame is held in the buffer! */ - receptionBuffer.reserve(this->frameSize); - receptionBuffer.resize(this->frameSize); - - if(timeoutSeconds == -1) { - receptionTimeout = DEFAULT_TIMEOUT; - } - else { - receptionTimeout = timevalOperations::toTimeval(timeoutSeconds); - } -} - -TcWinUdpPollingTask::~TcWinUdpPollingTask() {} - -ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) { - /* Sender Address is cached here. */ - struct sockaddr_in senderAddress; - int senderAddressSize = sizeof(senderAddress); - - /* Poll for new UDP datagrams in permanent loop. */ - while(true) { - int bytesReceived = recvfrom( - serverUdpSocket, - reinterpret_cast(receptionBuffer.data()), - frameSize, - receptionFlags, - reinterpret_cast(&senderAddress), - &senderAddressSize - ); - if(bytesReceived == SOCKET_ERROR) { - /* Handle error */ -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TcWinUdpPollingTask::performOperation: Reception error." << std::endl; -#endif - tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::RECVFROM_CALL, 1000); - continue; - } -#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1 - sif::debug << "TcWinUdpPollingTask::performOperation: " << bytesReceived << - " bytes received" << std::endl; -#endif - - ReturnValue_t result = handleSuccessfullTcRead(bytesReceived); - if(result != HasReturnvaluesIF::RETURN_FAILED) { - - } - tmtcBridge->checkAndSetClientAddress(senderAddress); - } - return HasReturnvaluesIF::RETURN_OK; -} - - -ReturnValue_t TcWinUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) { - store_address_t storeId; - -#if FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1 - arrayprinter::print(receptionBuffer.data(), bytesRead); -#endif - - ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRead); - if (result != HasReturnvaluesIF::RETURN_OK) { -#if FSFW_VERBOSE_LEVEL >= 1 -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning<< "TcWinUdpPollingTask::transferPusToSoftwareBus: Data storage failed." << - std::endl; - sif::warning << "Packet size: " << bytesRead << std::endl; -#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ -#endif /* FSFW_VERBOSE_LEVEL >= 1 */ - return HasReturnvaluesIF::RETURN_FAILED; - } - - TmTcMessage message(storeId); - - result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message); - if (result != HasReturnvaluesIF::RETURN_OK) { -#if FSFW_VERBOSE_LEVEL >= 1 -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TcWinUdpPollingTask::handleSuccessfullTcRead: " - " Sending message to queue failed" << std::endl; -#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ -#endif /* FSFW_VERBOSE_LEVEL >= 1 */ - tcStore->deleteData(storeId); - } - return result; -} - -ReturnValue_t TcWinUdpPollingTask::initialize() { - tcStore = objectManager->get(objects::TC_STORE); - if (tcStore == nullptr) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TcWinUdpPollingTask::initialize: TC store uninitialized!" << std::endl; -#endif - return ObjectManagerIF::CHILD_INIT_FAILED; - } - - tmtcBridge = objectManager->get(tmtcBridgeId); - if(tmtcBridge == nullptr) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TcWinUdpPollingTask::initialize: Invalid TMTC bridge object!" << - std::endl; -#endif - return ObjectManagerIF::CHILD_INIT_FAILED; - } - return HasReturnvaluesIF::RETURN_OK; -} - -ReturnValue_t TcWinUdpPollingTask::initializeAfterTaskCreation() { - /* Initialize the destination after task creation. This ensures - that the destination has already been set in the TMTC bridge. */ - targetTcDestination = tmtcBridge->getRequestQueue(); - /* The server socket is set up in the bridge intialization. Calling this function here - ensures that it is set up properly in any case*/ - serverUdpSocket = tmtcBridge->serverSocket; - return HasReturnvaluesIF::RETURN_OK; -} - -void TcWinUdpPollingTask::setTimeout(double timeoutSeconds) { - DWORD timeoutMs = timeoutSeconds * 1000.0; - int result = setsockopt(serverUdpSocket, SOL_SOCKET, SO_RCVTIMEO, - reinterpret_cast(&timeoutMs), sizeof(DWORD)); - if(result == -1) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TcWinUdpPollingTask::TcSocketPollingTask: Setting " - "receive timeout failed with " << strerror(errno) << std::endl; -#endif - } -} diff --git a/osal/windows/tcpipHelpers.cpp b/osal/windows/tcpipHelpers.cpp index ef07f5ca..03278a92 100644 --- a/osal/windows/tcpipHelpers.cpp +++ b/osal/windows/tcpipHelpers.cpp @@ -1,4 +1,4 @@ -#include "tcpipHelpers.h" +#include "../common/tcpipHelpers.h" #include #include "../../tasks/TaskFactory.h" diff --git a/osal/windows/winTaskHelpers.cpp b/osal/windows/winTaskHelpers.cpp new file mode 100644 index 00000000..e96cd70e --- /dev/null +++ b/osal/windows/winTaskHelpers.cpp @@ -0,0 +1,130 @@ +#include +#include + +std::mutex nameMapLock; +std::map taskNameMap; + +TaskPriority tasks::makeWinPriority(PriorityClass prioClass, PriorityNumber prioNumber) { + return (static_cast(prioClass) << 16) | static_cast (prioNumber); +} + +void tasks::getWinPriorityParameters(TaskPriority priority, + DWORD& priorityClass, int& priorityNumber) { + PriorityClass classInternal = static_cast(priority >> 16 & 0xff); + PriorityNumber numberInternal = static_cast(priority & 0xff); + switch(classInternal) { + case(CLASS_IDLE): { + priorityClass = IDLE_PRIORITY_CLASS; + break; + } + case(CLASS_BELOW_NORMAL): { + priorityClass = BELOW_NORMAL_PRIORITY_CLASS; + break; + } + case(CLASS_NORMAL): { + priorityClass = NORMAL_PRIORITY_CLASS; + break; + } + case(CLASS_ABOVE_NORMAL): { + priorityClass = ABOVE_NORMAL_PRIORITY_CLASS; + break; + } + case(CLASS_HIGH): { + priorityClass = HIGH_PRIORITY_CLASS; + break; + } + case(CLASS_REALTIME): { + priorityClass = REALTIME_PRIORITY_CLASS; + break; + } + default: { + priorityClass = NORMAL_PRIORITY_CLASS; + } + } + + switch(numberInternal) { + case(IDLE): { + priorityNumber = THREAD_PRIORITY_IDLE; + break; + } + case(LOWEST): { + priorityNumber = THREAD_PRIORITY_LOWEST; + break; + } + case(BELOW_NORMAL): { + priorityNumber = THREAD_PRIORITY_BELOW_NORMAL; + break; + } + case(NORMAL): { + priorityNumber = THREAD_PRIORITY_NORMAL; + break; + } + case(ABOVE_NORMAL): { + priorityNumber = THREAD_PRIORITY_ABOVE_NORMAL; + break; + } + case(HIGHEST): { + priorityNumber = THREAD_PRIORITY_HIGHEST; + break; + } + case(CRITICAL): { + priorityNumber = THREAD_PRIORITY_TIME_CRITICAL; + break; + } + default: { + priorityNumber = THREAD_PRIORITY_NORMAL; + } + } +} + +ReturnValue_t tasks::setTaskPriority(HANDLE nativeHandle, TaskPriority priority) { + /* List of possible priority classes: + https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setpriorityclass + And respective thread priority numbers: + https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities + */ + DWORD dwPriorityClass = 0; + int nPriorityNumber = 0; + tasks::getWinPriorityParameters(priority, dwPriorityClass, nPriorityNumber); + int result = SetPriorityClass( + reinterpret_cast(nativeHandle), + dwPriorityClass); + if(result != 0) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "PeriodicTask: Windows SetPriorityClass failed with code " + << GetLastError() << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; +#endif + } + result = SetThreadPriority( + reinterpret_cast(nativeHandle), + nPriorityNumber); + if(result != 0) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "PeriodicTask: Windows SetPriorityClass failed with code " + << GetLastError() << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; +#endif + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t tasks::insertTaskName(std::thread::id threadId, std::string taskName) { + std::lock_guard lg(nameMapLock); + auto returnPair = taskNameMap.emplace(threadId, taskName); + if(not returnPair.second) { + return HasReturnvaluesIF::RETURN_FAILED; + } + return HasReturnvaluesIF::RETURN_OK; +} + +std::string tasks::getTaskName(std::thread::id threadId) { + std::lock_guard lg(nameMapLock); + auto resultIter = taskNameMap.find(threadId); + if(resultIter != taskNameMap.end()) { + return resultIter->second; + } + else { + return "Unknown task"; + } +} diff --git a/osal/windows/winTaskHelpers.h b/osal/windows/winTaskHelpers.h new file mode 100644 index 00000000..d17b611a --- /dev/null +++ b/osal/windows/winTaskHelpers.h @@ -0,0 +1,40 @@ +#include "../../tasks/TaskFactory.h" + +#include +#include + +#ifdef _WIN32 + +namespace tasks { + +enum PriorityClass: uint16_t { + CLASS_IDLE, + CLASS_BELOW_NORMAL, + CLASS_NORMAL, + CLASS_ABOVE_NORMAL, + CLASS_HIGH, + CLASS_REALTIME +}; +enum PriorityNumber: uint16_t { + IDLE, + LOWEST, + BELOW_NORMAL, + NORMAL, + ABOVE_NORMAL, + HIGHEST, + CRITICAL +}; +TaskPriority makeWinPriority(PriorityClass prioClass = PriorityClass::CLASS_NORMAL, + PriorityNumber prioNumber = PriorityNumber::NORMAL); +void getWinPriorityParameters(TaskPriority priority, DWORD& priorityClass, + int& priorityNumber); + +ReturnValue_t setTaskPriority(HANDLE nativeHandle, TaskPriority priority); + +ReturnValue_t insertTaskName(std::thread::id threadId, std::string taskName); +std::string getTaskName(std::thread::id threadId); + +} + +#endif + diff --git a/tasks/TaskFactory.h b/tasks/TaskFactory.h index 03b5163c..4fba345f 100644 --- a/tasks/TaskFactory.h +++ b/tasks/TaskFactory.h @@ -22,9 +22,23 @@ public: /** * Creates a new periodic task and returns the interface pointer. * @param name_ Name of the task - * @param taskPriority_ Priority of the task + * @param taskPriority_ + * Priority of the task. This value might have different ranges for the various OSALs. + * Linux: Value ranging from 0 to 99 with 99 being the highest value. + * Host: Value can be retrieved by using the #tasks::makeWinPriority function. + * FreeRTOS: Value depends on the FreeRTOS configuration, higher number means higher priority + * RTEMS: Values ranging from 0 to 99 with 99 being the highest value. + * * @param stackSize_ Stack Size of the task + * This value might have different recommended ranges for the various OSALs. + * Linux: Lowest limit is the PeriodicTaskIF::MINIMUM_STACK_SIZE value + * Host: Value is ignored for now because the C++ threading abstraction layer is used. + * FreeRTOS: Stack size in bytes. It is recommended to specify at least 1kB of stack for + * FSFW tasks, but the lowest possible size is specified in the FreeRTOSConfig.h file. + * RTEMS: Lowest limit is the PeriodicTaskIF::MINIMUM_STACK_SIZE value. + * * @param period_ Period of the task + * * @param deadLineMissedFunction_ Function to be called if a deadline was missed * @return PeriodicTaskIF* Pointer to the newly created Task */ diff --git a/tasks/Typedef.h b/tasks/Typedef.h index 07d96b00..55f6bda2 100644 --- a/tasks/Typedef.h +++ b/tasks/Typedef.h @@ -2,7 +2,7 @@ #define FRAMEWORK_TASKS_TYPEDEF_H_ typedef const char* TaskName; -typedef uint8_t TaskPriority; +typedef uint32_t TaskPriority; typedef size_t TaskStackSize; typedef double TaskPeriod; typedef void (*TaskDeadlineMissedFunction)();