diff --git a/osal/common/CMakeLists.txt b/osal/common/CMakeLists.txt index af76484d3..b7c8c033a 100644 --- a/osal/common/CMakeLists.txt +++ b/osal/common/CMakeLists.txt @@ -5,6 +5,7 @@ if(DEFINED WIN32 OR DEFINED UNIX) UdpTcPollingTask.cpp UdpTmTcBridge.cpp TcpTmTcServer.cpp + TcpTmTcBridge.cpp ) endif() diff --git a/osal/common/TcpTmTcBridge.cpp b/osal/common/TcpTmTcBridge.cpp new file mode 100644 index 000000000..24fab9a90 --- /dev/null +++ b/osal/common/TcpTmTcBridge.cpp @@ -0,0 +1,77 @@ +#include "TcpTmTcBridge.h" +#include "tcpipHelpers.h" + +#include +#include +#include + +#ifdef _WIN32 + +#include + +#elif defined(__unix__) + +#include +#include + +#endif + +const std::string TcpTmTcBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT; + +TcpTmTcBridge::TcpTmTcBridge(object_id_t objectId, object_id_t tcDestination, + object_id_t tmStoreId, object_id_t tcStoreId): + TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) { + mutex = MutexFactory::instance()->createMutex(); + // Connection is always up, TM is requested by connecting to server and receiving packets + registerCommConnect(); +} + +ReturnValue_t TcpTmTcBridge::initialize() { + ReturnValue_t result = TmTcBridge::initialize(); + if(result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TcpTmTcBridge::initialize: TmTcBridge initialization failed!" + << std::endl; +#else + sif::printError("TcpTmTcBridge::initialize: TmTcBridge initialization failed!\n"); +#endif + return result; + } + + return HasReturnvaluesIF::RETURN_OK; +} + +TcpTmTcBridge::~TcpTmTcBridge() { + if(mutex != nullptr) { + MutexFactory::instance()->deleteMutex(mutex); + } +} + +ReturnValue_t TcpTmTcBridge::handleTm() { + // Simply store the telemetry in the FIFO, the server will use it to access the TM + MutexGuard guard(mutex, timeoutType, mutexTimeoutMs); + TmTcMessage message; + ReturnValue_t status = HasReturnvaluesIF::RETURN_OK; + for (ReturnValue_t result = tmTcReceptionQueue->receiveMessage(&message); + result == HasReturnvaluesIF::RETURN_OK; + result = tmTcReceptionQueue->receiveMessage(&message)) + { + status = storeDownlinkData(&message); + if(status != HasReturnvaluesIF::RETURN_OK) { + break; + } + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t TcpTmTcBridge::sendTm(const uint8_t *data, size_t dataLen) { + // Not used. The Server uses the FIFO to access and send the telemetry. + return HasReturnvaluesIF::RETURN_OK; +} + + +void TcpTmTcBridge::setMutexProperties(MutexIF::TimeoutType timeoutType, + dur_millis_t timeoutMs) { + this->timeoutType = timeoutType; + this->mutexTimeoutMs = timeoutMs; +} diff --git a/osal/common/TcpTmTcBridge.h b/osal/common/TcpTmTcBridge.h new file mode 100644 index 000000000..6cfacb9fa --- /dev/null +++ b/osal/common/TcpTmTcBridge.h @@ -0,0 +1,71 @@ +#ifndef FSFW_OSAL_COMMON_TCPTMTCBRIDGE_H_ +#define FSFW_OSAL_COMMON_TCPTMTCBRIDGE_H_ + +#include "TcpIpBase.h" +#include "../../tmtcservices/TmTcBridge.h" + +#ifdef _WIN32 + +#include + +#elif defined(__unix__) + +#include + +#endif + +#include + +/** + * @brief This class should be used with the TcpTmTcServer to implement a TCP server + * for receiving and sending PUS telemetry and telecommands (TMTC) + * @details + * This bridge tasks takes care of filling a FIFO which generated telemetry. The TcpTmTcServer + * will take care of sending the telemetry stored in the FIFO if a client connects to the + * server. This bridge will also be the default destination for telecommands, but the telecommands + * will be relayed to a specified tcDestination directly. + */ +class TcpTmTcBridge: + public TmTcBridge { + friend class TcpTmTcServer; +public: + /* The ports chosen here should not be used by any other process. */ + static const std::string DEFAULT_UDP_SERVER_PORT; + + /** + * Constructor + * @param objectId Object ID of the TcpTmTcBridge. + * @param tcDestination Destination for received TC packets. Any received telecommands will + * be sent there directly. The destination object needs to implement + * AcceptsTelecommandsIF. + * @param tmStoreId TM store object ID. It is recommended to the default object ID + * @param tcStoreId TC store object ID. It is recommended to the default object ID + */ + TcpTmTcBridge(object_id_t objectId, object_id_t tcDestination, + object_id_t tmStoreId = objects::TM_STORE, + object_id_t tcStoreId = objects::TC_STORE); + virtual~ TcpTmTcBridge(); + + /** + * Set properties of internal mutex. + */ + void setMutexProperties(MutexIF::TimeoutType timeoutType, dur_millis_t timeoutMs); + + ReturnValue_t initialize() override; + + +protected: + ReturnValue_t handleTm() override; + virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override; + +private: + + //! Access to the FIFO needs to be mutex protected because it is used by the bridge and + //! the server. + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING; + dur_millis_t mutexTimeoutMs = 20; + MutexIF* mutex; +}; + +#endif /* FSFW_OSAL_COMMON_TCPTMTCBRIDGE_H_ */ + diff --git a/osal/common/TcpTmTcServer.cpp b/osal/common/TcpTmTcServer.cpp index e1a26fe7f..28fab4224 100644 --- a/osal/common/TcpTmTcServer.cpp +++ b/osal/common/TcpTmTcServer.cpp @@ -1,7 +1,15 @@ #include "TcpTmTcServer.h" +#include "TcpTmTcBridge.h" #include "tcpipHelpers.h" + #include "../../platform.h" +#include "../../container/SharedRingBuffer.h" +#include "../../ipc/MessageQueueSenderIF.h" +#include "../../ipc/MutexGuard.h" +#include "../../objectmanager/ObjectManagerIF.h" + #include "../../serviceinterface/ServiceInterface.h" +#include "../../tmtcservices/TmTcMessage.h" #ifdef PLATFORM_WIN #include @@ -10,12 +18,16 @@ #include #endif -const std::string TcpTmTcServer::DEFAULT_TCP_SERVER_PORT = "7301"; -const std::string TcpTmTcServer::DEFAULT_TCP_CLIENT_PORT = "7302"; +#ifndef FSFW_TCP_RECV_WIRETAPPING_ENABLED +#define FSFW_TCP_RECV_WIRETAPPING_ENABLED 0 +#endif -TcpTmTcServer::TcpTmTcServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge, - std::string customTcpServerPort): - SystemObject(objectId), tcpPort(customTcpServerPort) { +const std::string TcpTmTcServer::DEFAULT_TCP_SERVER_PORT = "7303"; + +TcpTmTcServer::TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge, + size_t receptionBufferSize, std::string customTcpServerPort): + SystemObject(objectId), tmtcBridgeId(tmtcTcpBridge), + tcpPort(customTcpServerPort), receptionBuffer(receptionBufferSize) { if(tcpPort == "") { tcpPort = DEFAULT_TCP_SERVER_PORT; } @@ -29,6 +41,18 @@ ReturnValue_t TcpTmTcServer::initialize() { return result; } + tcStore = objectManager->get(objects::TC_STORE); + if (tcStore == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "TcpTmTcServer::initialize: TC store uninitialized!" << std::endl; +#else + sif::printError("TcpTmTcServer::initialize: TC store uninitialized!\n"); +#endif + return ObjectManagerIF::CHILD_INIT_FAILED; + } + + tmtcBridge = objectManager->get(tmtcBridgeId); + int retval = 0; struct addrinfo *addrResult = nullptr; struct addrinfo hints = {}; @@ -38,34 +62,25 @@ ReturnValue_t TcpTmTcServer::initialize() { hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; + // Listen to all addresses (0.0.0.0) by using AI_PASSIVE in the hint flags retval = getaddrinfo(nullptr, tcpPort.c_str(), &hints, &addrResult); if (retval != 0) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TcWinTcpServer::TcpTmTcServer: Retrieving address info failed!" << - std::endl; -#endif handleError(Protocol::TCP, ErrorSources::GETADDRINFO_CALL); return HasReturnvaluesIF::RETURN_FAILED; } - /* Open TCP (stream) socket */ + // Open TCP (stream) socket listenerTcpSocket = socket(addrResult->ai_family, addrResult->ai_socktype, addrResult->ai_protocol); if(listenerTcpSocket == INVALID_SOCKET) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TcWinTcpServer::TcWinTcpServer: Socket creation failed!" << std::endl; -#endif freeaddrinfo(addrResult); handleError(Protocol::TCP, ErrorSources::SOCKET_CALL); return HasReturnvaluesIF::RETURN_FAILED; } + // Bind to the address found by getaddrinfo retval = bind(listenerTcpSocket, addrResult->ai_addr, static_cast(addrResult->ai_addrlen)); if(retval == SOCKET_ERROR) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TcWinTcpServer::TcpTmTcServer: Binding socket failed!" << - std::endl; -#endif freeaddrinfo(addrResult); handleError(Protocol::TCP, ErrorSources::BIND_CALL); return HasReturnvaluesIF::RETURN_FAILED; @@ -82,49 +97,130 @@ TcpTmTcServer::~TcpTmTcServer() { 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_t clientSocket = 0; - sockaddr clientSockAddr = {}; - socklen_t connectorSockAddrLen = 0; + // If a connection is accepted, the corresponding socket will be assigned to the new socket + socket_t connSocket = 0; + // sockaddr clientSockAddr = {}; + // socklen_t connectorSockAddrLen = 0; int retval = 0; - /* Listen for connection requests permanently for lifetime of program */ + // Listen for connection requests permanently for lifetime of program while(true) { - retval = listen(listenerTcpSocket, currentBacklog); + retval = listen(listenerTcpSocket, tcpBacklog); if(retval == SOCKET_ERROR) { handleError(Protocol::TCP, ErrorSources::LISTEN_CALL, 500); continue; } - clientSocket = accept(listenerTcpSocket, &clientSockAddr, &connectorSockAddrLen); + //connSocket = accept(listenerTcpSocket, &clientSockAddr, &connectorSockAddrLen); + connSocket = accept(listenerTcpSocket, nullptr, nullptr); - if(clientSocket == INVALID_SOCKET) { + if(connSocket == INVALID_SOCKET) { handleError(Protocol::TCP, ErrorSources::ACCEPT_CALL, 500); - closeSocket(clientSocket); + closeSocket(connSocket); continue; }; - retval = recv(clientSocket, reinterpret_cast(receptionBuffer.data()), - receptionBuffer.size(), 0); - if(retval > 0) { -#if FSFW_TCP_RCV_WIRETAPPING_ENABLED == 1 - sif::info << "TcpTmTcServer::performOperation: Received " << retval << " bytes." - std::endl; -#endif - handleError(Protocol::TCP, ErrorSources::RECV_CALL, 500); - } - else if(retval == 0) { + handleServerOperation(connSocket); + // Done, shut down connection and go back to listening for client requests + retval = shutdown(connSocket, SHUT_SEND); + if(retval != 0) { + handleError(Protocol::TCP, ErrorSources::SHUTDOWN_CALL); } - else { - - } - - /* Done, shut down connection */ - retval = shutdown(clientSocket, SHUT_SEND); - closeSocket(clientSocket); + closeSocket(connSocket); } return HasReturnvaluesIF::RETURN_OK; } +ReturnValue_t TcpTmTcServer::initializeAfterTaskCreation() { + if(tmtcBridge == nullptr) { + return ObjectManagerIF::CHILD_INIT_FAILED; + } + /* Initialize the destination after task creation. This ensures + that the destination has already been set in the TMTC bridge. */ + targetTcDestination = tmtcBridge->getRequestQueue(); + tcStore = tmtcBridge->tcStore; + tmStore = tmtcBridge->tmStore; + return HasReturnvaluesIF::RETURN_OK; +} +void TcpTmTcServer::handleServerOperation(socket_t connSocket) { + int retval = 0; + do { + // Read all telecommands sent by the client + retval = recv(connSocket, + reinterpret_cast(receptionBuffer.data()), + receptionBuffer.capacity(), + tcpFlags); + if (retval > 0) { + handleTcReception(retval); + } + else if(retval == 0) { + // Client has finished sending telecommands, send telemetry now + handleTmSending(connSocket); + } + else { + // Should not happen + tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::RECV_CALL); + } + } while(retval > 0); +} + +ReturnValue_t TcpTmTcServer::handleTcReception(size_t bytesRecvd) { +#if FSFW_TCP_RECV_WIRETAPPING_ENABLED == 1 + arrayprinter::print(receptionBuffer.data(), bytesRead); +#endif + store_address_t storeId; + ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRecvd); + if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning<< "TcpTmTcServer::handleServerOperation: Data storage failed." << std::endl; + sif::warning << "Packet size: " << bytesRecvd << std::endl; +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ + } + + 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; +} + +void TcpTmTcServer::setTcpBacklog(uint8_t tcpBacklog) { + this->tcpBacklog = tcpBacklog; +} + +ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket) { + // Access to the FIFO is mutex protected because it is filled by the bridge + MutexGuard(tmtcBridge->mutex, tmtcBridge->timeoutType, tmtcBridge->mutexTimeoutMs); + store_address_t storeId; + while((not tmtcBridge->tmFifo->empty()) and + (tmtcBridge->packetSentCounter < tmtcBridge->sentPacketsPerCycle)) { + tmtcBridge->tmFifo->retrieve(&storeId); + + // Using the store accessor will take care of deleting TM from the store automatically + ConstStorageAccessor storeAccessor(storeId); + ReturnValue_t result = tmStore->getData(storeId, storeAccessor); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + int retval = send(connSocket, + reinterpret_cast(storeAccessor.data()), + storeAccessor.size(), + tcpTmFlags); + if(retval != static_cast(storeAccessor.size())) { + tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::SEND_CALL); + } + } + return HasReturnvaluesIF::RETURN_OK; +} diff --git a/osal/common/TcpTmTcServer.h b/osal/common/TcpTmTcServer.h index 91c579e5c..f7c36d69c 100644 --- a/osal/common/TcpTmTcServer.h +++ b/osal/common/TcpTmTcServer.h @@ -1,9 +1,14 @@ -#ifndef FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_ -#define FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_ +#ifndef FSFW_OSAL_COMMON_TCP_TMTC_SERVER_H_ +#define FSFW_OSAL_COMMON_TCP_TMTC_SERVER_H_ #include "TcpIpBase.h" + #include "../../platform.h" +#include "../../ipc/messageQueueDefinitions.h" +#include "../../ipc/MessageQueueIF.h" +#include "../../objectmanager/frameworkObjects.h" #include "../../objectmanager/SystemObject.h" +#include "../../storagemanager/StorageManagerIF.h" #include "../../tasks/ExecutableObjectIF.h" #ifdef PLATFORM_UNIX @@ -13,13 +18,23 @@ #include #include -//! Debugging preprocessor define. -#define FSFW_TCP_RCV_WIRETAPPING_ENABLED 0 +class TcpTmTcBridge; /** - * @brief Windows TCP server used to receive telecommands on a Windows Host + * @brief TCP server implementation * @details - * Based on: https://docs.microsoft.com/en-us/windows/win32/winsock/complete-server-code + * This server will run for the whole program lifetime and will take care of serving client + * requests on a specified TCP server port. This server was written in a generic way and + * can be used on Unix and on Windows systems. + * + * If a connection is accepted, the server will read all telecommands sent by a client and then + * send all telemetry currently found in the TMTC bridge FIFO. + * + * Reading telemetry without sending telecommands is possible by connecting, shutting down the + * send operation immediately and then reading the telemetry. It is therefore recommended to + * connect to the server regularly, even if no telecommands need to be sent. + * + * The server will listen to a specific port on all addresses (0.0.0.0). */ class TcpTmTcServer: public SystemObject, @@ -28,27 +43,51 @@ class TcpTmTcServer: 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; - TcpTmTcServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge, + static constexpr size_t ETHERNET_MTU_SIZE = 1500; + + /** + * TCP Server Constructor + * @param objectId Object ID of the TCP Server + * @param tmtcTcpBridge Object ID of the TCP TMTC Bridge object + * @param receptionBufferSize This will be the size of the reception buffer. Default buffer + * size will be the Ethernet MTU size + * @param customTcpServerPort The user can specify another port than the default (7301) here. + */ + TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge, + size_t receptionBufferSize = ETHERNET_MTU_SIZE + 1, std::string customTcpServerPort = ""); virtual~ TcpTmTcServer(); + void setTcpBacklog(uint8_t tcpBacklog); + ReturnValue_t initialize() override; ReturnValue_t performOperation(uint8_t opCode) override; + ReturnValue_t initializeAfterTaskCreation() override; +protected: + StorageManagerIF* tcStore = nullptr; + StorageManagerIF* tmStore = nullptr; private: + //! TMTC bridge is cached. + object_id_t tmtcBridgeId = objects::NO_OBJECT; + TcpTmTcBridge* tmtcBridge = nullptr; std::string tcpPort; + int tcpFlags = 0; socket_t listenerTcpSocket = 0; struct sockaddr tcpAddress; + MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE; int tcpAddrLen = sizeof(tcpAddress); - int currentBacklog = 3; + int tcpBacklog = 3; std::vector receptionBuffer; int tcpSockOpt = 0; + int tcpTmFlags = 0; - + void handleServerOperation(socket_t connSocket); + ReturnValue_t handleTcReception(size_t bytesRecvd); + ReturnValue_t handleTmSending(socket_t connSocket); }; -#endif /* FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_ */ +#endif /* FSFW_OSAL_COMMON_TCP_TMTC_SERVER_H_ */ diff --git a/osal/common/UdpTcPollingTask.cpp b/osal/common/UdpTcPollingTask.cpp index 68108323a..877e78835 100644 --- a/osal/common/UdpTcPollingTask.cpp +++ b/osal/common/UdpTcPollingTask.cpp @@ -15,9 +15,9 @@ #define FSFW_UDP_RECV_WIRETAPPING_ENABLED 0 UdpTcPollingTask::UdpTcPollingTask(object_id_t objectId, - object_id_t tmtcUnixUdpBridge, size_t maxRecvSize, + object_id_t tmtcUdpBridge, size_t maxRecvSize, double timeoutSeconds): SystemObject(objectId), - tmtcBridgeId(tmtcUnixUdpBridge) { + tmtcBridgeId(tmtcUdpBridge) { if(frameSize > 0) { this->frameSize = frameSize; } diff --git a/osal/common/UdpTcPollingTask.h b/osal/common/UdpTcPollingTask.h index 052eced52..9a680fb81 100644 --- a/osal/common/UdpTcPollingTask.h +++ b/osal/common/UdpTcPollingTask.h @@ -1,5 +1,5 @@ -#ifndef FSFW_OSAL_WINDOWS_TCSOCKETPOLLINGTASK_H_ -#define FSFW_OSAL_WINDOWS_TCSOCKETPOLLINGTASK_H_ +#ifndef FSFW_OSAL_COMMON_UDPTCPOLLINGTASK_H_ +#define FSFW_OSAL_COMMON_UDPTCPOLLINGTASK_H_ #include "UdpTmTcBridge.h" #include "../../objectmanager/SystemObject.h" @@ -9,8 +9,11 @@ #include /** - * @brief This class should be used with the UdpTmTcBridge to implement a UDP server + * @brief This class can be used with the UdpTmTcBridge to implement a UDP server * for receiving and sending PUS TMTC. + * @details + * This task is exclusively used to poll telecommands from a given socket and transfer them + * to the FSFW software bus. It used the blocking recvfrom call to do this. */ class UdpTcPollingTask: public TcpIpBase, @@ -22,7 +25,7 @@ public: //! 0.5 default milliseconds timeout for now. static constexpr timeval DEFAULT_TIMEOUT = {0, 500}; - UdpTcPollingTask(object_id_t objectId, object_id_t tmtcUnixUdpBridge, + UdpTcPollingTask(object_id_t objectId, object_id_t tmtcUdpBridge, size_t maxRecvSize = 0, double timeoutSeconds = -1); virtual~ UdpTcPollingTask(); @@ -45,8 +48,6 @@ private: object_id_t tmtcBridgeId = objects::NO_OBJECT; 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; std::vector receptionBuffer; @@ -57,4 +58,4 @@ private: ReturnValue_t handleSuccessfullTcRead(size_t bytesRead); }; -#endif /* FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ */ +#endif /* FSFW_OSAL_COMMON_UDPTCPOLLINGTASK_H_ */ diff --git a/osal/common/UdpTmTcBridge.cpp b/osal/common/UdpTmTcBridge.cpp index 2ecc7b7ca..4c83385ba 100644 --- a/osal/common/UdpTmTcBridge.cpp +++ b/osal/common/UdpTmTcBridge.cpp @@ -13,12 +13,14 @@ #endif //! Debugging preprocessor define. +#ifndef FSFW_UDP_SEND_WIRETAPPING_ENABLED #define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0 +#endif const std::string UdpTmTcBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT; UdpTmTcBridge::UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination, - object_id_t tmStoreId, object_id_t tcStoreId, std::string udpServerPort): + std::string udpServerPort, object_id_t tmStoreId, object_id_t tcStoreId): TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) { if(udpServerPort == "") { this->udpServerPort = DEFAULT_UDP_SERVER_PORT; @@ -35,7 +37,7 @@ ReturnValue_t UdpTmTcBridge::initialize() { ReturnValue_t result = TmTcBridge::initialize(); if(result != HasReturnvaluesIF::RETURN_OK) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TmTcUdpBridge::initialize: TmTcBridge initialization failed!" + sif::error << "UdpTmTcBridge::initialize: TmTcBridge initialization failed!" << std::endl; #endif return result; @@ -51,10 +53,10 @@ ReturnValue_t UdpTmTcBridge::initialize() { /* Tell the user that we could not find a usable */ /* Winsock DLL. */ #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TmTcUdpBridge::TmTcUdpBridge: WSAStartup failed with error: " << + sif::error << "UdpTmTcBridge::UdpTmTcBridge: WSAStartup failed with error: " << err << std::endl; #else - sif::printError("TmTcUdpBridge::TmTcUdpBridge: WSAStartup failed with error: %d\n", + sif::printError("UdpTmTcBridge::UdpTmTcBridge: WSAStartup failed with error: %d\n", err); #endif return HasReturnvaluesIF::RETURN_FAILED; @@ -75,19 +77,12 @@ ReturnValue_t UdpTmTcBridge::initialize() { 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 << "TmTcUdpBridge::TmTcUdpBridge: Retrieving address info failed!" << - std::endl; -#endif + tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::GETADDRINFO_CALL); return HasReturnvaluesIF::RETURN_FAILED; } serverSocket = socket(addrResult->ai_family, addrResult->ai_socktype, addrResult->ai_protocol); if(serverSocket == INVALID_SOCKET) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "TmTcUdpBridge::TmTcUdpBridge: Could not open UDP socket!" << - std::endl; -#endif freeaddrinfo(addrResult); tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::SOCKET_CALL); return HasReturnvaluesIF::RETURN_FAILED; @@ -99,10 +94,6 @@ ReturnValue_t UdpTmTcBridge::initialize() { retval = bind(serverSocket, addrResult->ai_addr, static_cast(addrResult->ai_addrlen)); if(retval != 0) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "TmTcUdpBridge::TmTcUdpBridge: Could not bind " - "local port (" << udpServerPort << ") to server socket!" << std::endl; -#endif freeaddrinfo(addrResult); tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::BIND_CALL); return HasReturnvaluesIF::RETURN_FAILED; diff --git a/osal/common/UdpTmTcBridge.h b/osal/common/UdpTmTcBridge.h index 360643bbf..7a346de57 100644 --- a/osal/common/UdpTmTcBridge.h +++ b/osal/common/UdpTmTcBridge.h @@ -1,5 +1,5 @@ -#ifndef FSFW_OSAL_WINDOWS_TMTCWINUDPBRIDGE_H_ -#define FSFW_OSAL_WINDOWS_TMTCWINUDPBRIDGE_H_ +#ifndef FSFW_OSAL_COMMON_TMTCUDPBRIDGE_H_ +#define FSFW_OSAL_COMMON_TMTCUDPBRIDGE_H_ #include "TcpIpBase.h" #include "../../platform.h" @@ -14,8 +14,13 @@ #include /** - * @brief This class should be used with the UdpTcPollingTask to implement a UDP server + * @brief This class can be used with the UdpTcPollingTask to implement a UDP server * for receiving and sending PUS TMTC. + * @details + * This bridge task will take care of sending telemetry back to a UDP client if a connection + * was established and store them in a FIFO if this was not done yet. It is also be the default + * destination for telecommands, but the telecommands will be relayed to a specified tcDestination + * directly. */ class UdpTmTcBridge: public TmTcBridge, @@ -26,7 +31,8 @@ public: static const std::string DEFAULT_UDP_SERVER_PORT; UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination, - object_id_t tmStoreId, object_id_t tcStoreId, std::string udpServerPort = ""); + std::string udpServerPort = "", object_id_t tmStoreId = objects::TM_STORE, + object_id_t tcStoreId = objects::TC_STORE); virtual~ UdpTmTcBridge(); /** @@ -53,5 +59,5 @@ private: MutexIF* mutex; }; -#endif /* FSFW_OSAL_HOST_TMTCWINUDPBRIDGE_H_ */ +#endif /* FSFW_OSAL_COMMON_TMTCUDPBRIDGE_H_ */ diff --git a/osal/common/tcpipCommon.cpp b/osal/common/tcpipCommon.cpp index 551e2a426..ca4dbf180 100644 --- a/osal/common/tcpipCommon.cpp +++ b/osal/common/tcpipCommon.cpp @@ -32,9 +32,18 @@ void tcpip::determineErrorStrings(Protocol protocol, ErrorSources errorSrc, std: else if(errorSrc == ErrorSources::RECVFROM_CALL) { srcString = "recvfrom call"; } + else if(errorSrc == ErrorSources::SEND_CALL) { + srcString = "send call"; + } + else if(errorSrc == ErrorSources::SENDTO_CALL) { + srcString = "sendto call"; + } else if(errorSrc == ErrorSources::GETADDRINFO_CALL) { srcString = "getaddrinfo call"; } + else if(errorSrc == ErrorSources::SHUTDOWN_CALL) { + srcString = "shutdown call"; + } else { srcString = "unknown call"; } diff --git a/osal/common/tcpipCommon.h b/osal/common/tcpipCommon.h index 22b914dc1..ce7a90cd1 100644 --- a/osal/common/tcpipCommon.h +++ b/osal/common/tcpipCommon.h @@ -29,7 +29,9 @@ enum class ErrorSources { RECVFROM_CALL, LISTEN_CALL, ACCEPT_CALL, - SENDTO_CALL + SEND_CALL, + SENDTO_CALL, + SHUTDOWN_CALL }; void determineErrorStrings(Protocol protocol, ErrorSources errorSrc, std::string& protStr, diff --git a/osal/windows/tcpipHelpers.cpp b/osal/windows/tcpipHelpers.cpp index 03278a92e..3dab9406c 100644 --- a/osal/windows/tcpipHelpers.cpp +++ b/osal/windows/tcpipHelpers.cpp @@ -50,8 +50,8 @@ void tcpip::handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t s sif::warning << "tcpip::handleError: " << protocolString << " | " << errorSrcString << " | " << infoString << std::endl; #else - sif::printWarning("tcpip::handleError: %s | %s | %s\n", protocolString, - errorSrcString, infoString); + sif::printWarning("tcpip::handleError: %s | %s | %s\n", protocolString.c_str(), + errorSrcString.c_str(), infoString.c_str()); #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ #endif /* FSFW_VERBOSE_LEVEL >= 1 */ diff --git a/tmtcservices/TmTcBridge.cpp b/tmtcservices/TmTcBridge.cpp index dcffac41e..1257ef890 100644 --- a/tmtcservices/TmTcBridge.cpp +++ b/tmtcservices/TmTcBridge.cpp @@ -183,8 +183,11 @@ ReturnValue_t TmTcBridge::storeDownlinkData(TmTcMessage *message) { if(tmFifo->full()) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::debug << "TmTcBridge::storeDownlinkData: TM downlink max. number " - << "of stored packet IDs reached! " << std::endl; + sif::warning << "TmTcBridge::storeDownlinkData: TM downlink max. number " + "of stored packet IDs reached!" << std::endl; +#else + sif::printWarning("TmTcBridge::storeDownlinkData: TM downlink max. number " + "of stored packet IDs reached!\n"); #endif if(overwriteOld) { tmFifo->retrieve(&storeId); diff --git a/tmtcservices/TmTcBridge.h b/tmtcservices/TmTcBridge.h index 0177648c0..d3e1c5471 100644 --- a/tmtcservices/TmTcBridge.h +++ b/tmtcservices/TmTcBridge.h @@ -150,8 +150,7 @@ protected: void printData(uint8_t * data, size_t dataLen); /** - * This fifo can be used to store downlink data - * which can not be sent at the moment. + * This FIFO can be used to store downlink data which can not be sent at the moment. */ DynamicFIFO* tmFifo = nullptr; uint8_t sentPacketsPerCycle = DEFAULT_STORED_DATA_SENT_PER_CYCLE; diff --git a/unittest/user/testcfg/objects/systemObjectList.h b/unittest/user/testcfg/objects/systemObjectList.h index 88b92131c..76f1ff90c 100644 --- a/unittest/user/testcfg/objects/systemObjectList.h +++ b/unittest/user/testcfg/objects/systemObjectList.h @@ -15,8 +15,8 @@ namespace objects { PUS_DISTRIBUTOR = 11, TM_FUNNEL = 12, - UDP_BRIDGE = 15, - UDP_POLLING_TASK = 16, + TCPIP_BRIDGE = 15, + TCPIP_HELPER = 16, TEST_ECHO_COM_IF = 20, TEST_DEVICE = 21,