Merge pull request 'FSFW Update' (#21) from mueller/unittest-fixes into eive/develop
Reviewed-on: #21
This commit is contained in:
commit
16fb87e02e
@ -13,6 +13,10 @@ option(FSFW_ADD_INTERNAL_TESTS "Add internal unit tests" ON)
|
|||||||
option(FSFW_ADD_UNITTESTS "Add regular unittests. Requires Catch2" OFF)
|
option(FSFW_ADD_UNITTESTS "Add regular unittests. Requires Catch2" OFF)
|
||||||
option(FSFW_ADD_HAL "Add Hardware Abstraction Layer" ON)
|
option(FSFW_ADD_HAL "Add Hardware Abstraction Layer" ON)
|
||||||
|
|
||||||
|
if(FSFW_ADD_UNITTESTS)
|
||||||
|
option(FSFW_CUSTOM_UNITTEST_RUNNER "Add FSFW custom main runner" ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Optional sources
|
# Optional sources
|
||||||
option(FSFW_ADD_PUS "Compile with PUS sources" ON)
|
option(FSFW_ADD_PUS "Compile with PUS sources" ON)
|
||||||
option(FSFW_ADD_MONITORING "Compile with monitoring components" ON)
|
option(FSFW_ADD_MONITORING "Compile with monitoring components" ON)
|
||||||
|
@ -20,6 +20,14 @@
|
|||||||
#define FSFW_TCP_RECV_WIRETAPPING_ENABLED 0
|
#define FSFW_TCP_RECV_WIRETAPPING_ENABLED 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef FSFW_USE_PUS_C_TELEMETRY
|
||||||
|
#define FSFW_USE_PUS_C_TELEMETRY 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FSFW_USE_PUS_C_TELECOMMANDS
|
||||||
|
#define FSFW_USE_PUS_C_TELECOMMANDS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
// Can be used for low-level debugging of the SPI bus
|
// Can be used for low-level debugging of the SPI bus
|
||||||
#ifndef FSFW_HAL_SPI_WIRETAPPING
|
#ifndef FSFW_HAL_SPI_WIRETAPPING
|
||||||
#define FSFW_HAL_SPI_WIRETAPPING 0
|
#define FSFW_HAL_SPI_WIRETAPPING 0
|
||||||
|
@ -5,8 +5,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Circular buffer implementation, useful for buffering
|
* @brief Circular buffer implementation, useful for buffering into data streams.
|
||||||
* into data streams.
|
|
||||||
* @details
|
* @details
|
||||||
* Note that the deleteData() has to be called to increment the read pointer.
|
* Note that the deleteData() has to be called to increment the read pointer.
|
||||||
* This class allocated dynamically, so
|
* This class allocated dynamically, so
|
||||||
@ -20,8 +19,8 @@ public:
|
|||||||
* @param size
|
* @param size
|
||||||
* @param overwriteOld If the ring buffer is overflowing at a write
|
* @param overwriteOld If the ring buffer is overflowing at a write
|
||||||
* operation, the oldest data will be overwritten.
|
* operation, the oldest data will be overwritten.
|
||||||
* @param maxExcessBytes These additional bytes will be allocated in addtion
|
* @param maxExcessBytes These additional bytes will be allocated in addition
|
||||||
* to the specified size to accomodate contiguous write operations
|
* to the specified size to accommodate continuous write operations
|
||||||
* with getFreeElement.
|
* with getFreeElement.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -32,10 +31,10 @@ public:
|
|||||||
* @param buffer
|
* @param buffer
|
||||||
* @param size
|
* @param size
|
||||||
* @param overwriteOld
|
* @param overwriteOld
|
||||||
* If the ring buffer is overflowing at a write operartion, the oldest data
|
* If the ring buffer is overflowing at a write operation, the oldest data
|
||||||
* will be overwritten.
|
* will be overwritten.
|
||||||
* @param maxExcessBytes
|
* @param maxExcessBytes
|
||||||
* If the buffer can accomodate additional bytes for contigous write
|
* If the buffer can accommodate additional bytes for contiguous write
|
||||||
* operations with getFreeElement, this is the maximum allowed additional
|
* operations with getFreeElement, this is the maximum allowed additional
|
||||||
* size
|
* size
|
||||||
*/
|
*/
|
||||||
@ -48,7 +47,7 @@ public:
|
|||||||
* Write to circular buffer and increment write pointer by amount.
|
* Write to circular buffer and increment write pointer by amount.
|
||||||
* @param data
|
* @param data
|
||||||
* @param amount
|
* @param amount
|
||||||
* @return -@c RETURN_OK if write operation was successfull
|
* @return -@c RETURN_OK if write operation was successful
|
||||||
* -@c RETURN_FAILED if
|
* -@c RETURN_FAILED if
|
||||||
*/
|
*/
|
||||||
ReturnValue_t writeData(const uint8_t* data, size_t amount);
|
ReturnValue_t writeData(const uint8_t* data, size_t amount);
|
||||||
@ -108,7 +107,7 @@ public:
|
|||||||
* Delete data by incrementing read pointer.
|
* Delete data by incrementing read pointer.
|
||||||
* @param amount
|
* @param amount
|
||||||
* @param deleteRemaining
|
* @param deleteRemaining
|
||||||
* If the amount specified is larger than the remaing size to read and this
|
* If the amount specified is larger than the remaining size to read and this
|
||||||
* is set to true, the remaining amount will be deleted as well
|
* is set to true, the remaining amount will be deleted as well
|
||||||
* @param trueAmount [out]
|
* @param trueAmount [out]
|
||||||
* If deleteRemaining was set to true, the amount deleted will be assigned
|
* If deleteRemaining was set to true, the amount deleted will be assigned
|
||||||
|
@ -5,11 +5,13 @@
|
|||||||
#include "TcpTmTcBridge.h"
|
#include "TcpTmTcBridge.h"
|
||||||
#include "tcpipHelpers.h"
|
#include "tcpipHelpers.h"
|
||||||
|
|
||||||
|
#include "fsfw/tmtcservices/SpacePacketParser.h"
|
||||||
|
#include "fsfw/tasks/TaskFactory.h"
|
||||||
|
#include "fsfw/globalfunctions/arrayprinter.h"
|
||||||
#include "fsfw/container/SharedRingBuffer.h"
|
#include "fsfw/container/SharedRingBuffer.h"
|
||||||
#include "fsfw/ipc/MessageQueueSenderIF.h"
|
#include "fsfw/ipc/MessageQueueSenderIF.h"
|
||||||
#include "fsfw/ipc/MutexGuard.h"
|
#include "fsfw/ipc/MutexGuard.h"
|
||||||
#include "fsfw/objectmanager/ObjectManager.h"
|
#include "fsfw/objectmanager/ObjectManager.h"
|
||||||
|
|
||||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||||
#include "fsfw/tmtcservices/TmTcMessage.h"
|
#include "fsfw/tmtcservices/TmTcMessage.h"
|
||||||
|
|
||||||
@ -20,19 +22,14 @@
|
|||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FSFW_TCP_RECV_WIRETAPPING_ENABLED
|
|
||||||
#define FSFW_TCP_RECV_WIRETAPPING_ENABLED 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const std::string TcpTmTcServer::DEFAULT_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT;
|
const std::string TcpTmTcServer::DEFAULT_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT;
|
||||||
|
|
||||||
TcpTmTcServer::TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge,
|
TcpTmTcServer::TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge,
|
||||||
size_t receptionBufferSize, std::string customTcpServerPort):
|
size_t receptionBufferSize, size_t ringBufferSize, std::string customTcpServerPort,
|
||||||
SystemObject(objectId), tmtcBridgeId(tmtcTcpBridge),
|
ReceptionModes receptionMode):
|
||||||
tcpPort(customTcpServerPort), receptionBuffer(receptionBufferSize) {
|
SystemObject(objectId), tmtcBridgeId(tmtcTcpBridge), receptionMode(receptionMode),
|
||||||
if(tcpPort == "") {
|
tcpConfig(customTcpServerPort), receptionBuffer(receptionBufferSize),
|
||||||
tcpPort = DEFAULT_SERVER_PORT;
|
ringBuffer(ringBufferSize, true) {
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t TcpTmTcServer::initialize() {
|
ReturnValue_t TcpTmTcServer::initialize() {
|
||||||
@ -43,6 +40,17 @@ ReturnValue_t TcpTmTcServer::initialize() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch(receptionMode) {
|
||||||
|
case(ReceptionModes::SPACE_PACKETS): {
|
||||||
|
spacePacketParser = new SpacePacketParser(validPacketIds);
|
||||||
|
if(spacePacketParser == nullptr) {
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
#if defined PLATFORM_UNIX
|
||||||
|
tcpConfig.tcpFlags |= MSG_DONTWAIT;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
tcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::TC_STORE);
|
tcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::TC_STORE);
|
||||||
if (tcStore == nullptr) {
|
if (tcStore == nullptr) {
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
@ -65,7 +73,7 @@ ReturnValue_t TcpTmTcServer::initialize() {
|
|||||||
hints.ai_flags = AI_PASSIVE;
|
hints.ai_flags = AI_PASSIVE;
|
||||||
|
|
||||||
// Listen to all addresses (0.0.0.0) by using AI_PASSIVE in the hint flags
|
// Listen to all addresses (0.0.0.0) by using AI_PASSIVE in the hint flags
|
||||||
retval = getaddrinfo(nullptr, tcpPort.c_str(), &hints, &addrResult);
|
retval = getaddrinfo(nullptr, tcpConfig.tcpPort.c_str(), &hints, &addrResult);
|
||||||
if (retval != 0) {
|
if (retval != 0) {
|
||||||
handleError(Protocol::TCP, ErrorSources::GETADDRINFO_CALL);
|
handleError(Protocol::TCP, ErrorSources::GETADDRINFO_CALL);
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
@ -107,7 +115,7 @@ ReturnValue_t TcpTmTcServer::performOperation(uint8_t opCode) {
|
|||||||
|
|
||||||
// Listen for connection requests permanently for lifetime of program
|
// Listen for connection requests permanently for lifetime of program
|
||||||
while(true) {
|
while(true) {
|
||||||
retval = listen(listenerTcpSocket, tcpBacklog);
|
retval = listen(listenerTcpSocket, tcpConfig.tcpBacklog);
|
||||||
if(retval == SOCKET_ERROR) {
|
if(retval == SOCKET_ERROR) {
|
||||||
handleError(Protocol::TCP, ErrorSources::LISTEN_CALL, 500);
|
handleError(Protocol::TCP, ErrorSources::LISTEN_CALL, 500);
|
||||||
continue;
|
continue;
|
||||||
@ -125,11 +133,12 @@ ReturnValue_t TcpTmTcServer::performOperation(uint8_t opCode) {
|
|||||||
handleServerOperation(connSocket);
|
handleServerOperation(connSocket);
|
||||||
|
|
||||||
// Done, shut down connection and go back to listening for client requests
|
// Done, shut down connection and go back to listening for client requests
|
||||||
retval = shutdown(connSocket, SHUT_SEND);
|
retval = shutdown(connSocket, SHUT_BOTH);
|
||||||
if(retval != 0) {
|
if(retval != 0) {
|
||||||
handleError(Protocol::TCP, ErrorSources::SHUTDOWN_CALL);
|
handleError(Protocol::TCP, ErrorSources::SHUTDOWN_CALL);
|
||||||
}
|
}
|
||||||
closeSocket(connSocket);
|
closeSocket(connSocket);
|
||||||
|
connSocket = 0;
|
||||||
}
|
}
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
@ -146,41 +155,85 @@ ReturnValue_t TcpTmTcServer::initializeAfterTaskCreation() {
|
|||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TcpTmTcServer::handleServerOperation(socket_t connSocket) {
|
void TcpTmTcServer::handleServerOperation(socket_t& connSocket) {
|
||||||
int retval = 0;
|
#if defined PLATFORM_WIN
|
||||||
do {
|
setSocketNonBlocking(connSocket);
|
||||||
// Read all telecommands sent by the client
|
#endif
|
||||||
retval = recv(connSocket,
|
|
||||||
|
while (true) {
|
||||||
|
int retval = recv(
|
||||||
|
connSocket,
|
||||||
reinterpret_cast<char*>(receptionBuffer.data()),
|
reinterpret_cast<char*>(receptionBuffer.data()),
|
||||||
receptionBuffer.capacity(),
|
receptionBuffer.capacity(),
|
||||||
tcpFlags);
|
tcpConfig.tcpFlags
|
||||||
if (retval > 0) {
|
);
|
||||||
handleTcReception(retval);
|
if(retval == 0) {
|
||||||
|
// Client closed connection
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(retval > 0) {
|
||||||
|
// The ring buffer was configured for overwrite, so the returnvalue does not need to
|
||||||
|
// be checked for now
|
||||||
|
ringBuffer.writeData(receptionBuffer.data(), retval);
|
||||||
|
}
|
||||||
|
else if(retval < 0) {
|
||||||
|
int errorValue = getLastSocketError();
|
||||||
|
#if defined PLATFORM_UNIX
|
||||||
|
int wouldBlockValue = EAGAIN;
|
||||||
|
#elif defined PLATFORM_WIN
|
||||||
|
int wouldBlockValue = WSAEWOULDBLOCK;
|
||||||
|
#endif
|
||||||
|
if(errorValue == wouldBlockValue) {
|
||||||
|
// No data available. Check whether any packets have been read, then send back
|
||||||
|
// telemetry if available
|
||||||
|
bool tcAvailable = false;
|
||||||
|
bool tmSent = false;
|
||||||
|
size_t availableReadData = ringBuffer.getAvailableReadData();
|
||||||
|
if(availableReadData > lastRingBufferSize) {
|
||||||
|
tcAvailable = true;
|
||||||
|
handleTcRingBufferData(availableReadData);
|
||||||
|
}
|
||||||
|
ReturnValue_t result = handleTmSending(connSocket, tmSent);
|
||||||
|
if(result == CONN_BROKEN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(not tcAvailable and not tmSent) {
|
||||||
|
TaskFactory::delayTask(tcpConfig.tcpLoopDelay);
|
||||||
}
|
}
|
||||||
else if(retval == 0) {
|
|
||||||
// Client has finished sending telecommands, send telemetry now
|
|
||||||
handleTmSending(connSocket);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Should not happen
|
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::RECV_CALL, 300);
|
||||||
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::RECV_CALL);
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while(retval > 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t TcpTmTcServer::handleTcReception(size_t bytesRecvd) {
|
ReturnValue_t TcpTmTcServer::handleTcReception(uint8_t* spacePacket, size_t packetSize) {
|
||||||
#if FSFW_TCP_RECV_WIRETAPPING_ENABLED == 1
|
if(wiretappingEnabled) {
|
||||||
arrayprinter::print(receptionBuffer.data(), bytesRead);
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::info << "Received TC:" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printInfo("Received TC:\n");
|
||||||
#endif
|
#endif
|
||||||
|
arrayprinter::print(spacePacket, packetSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(spacePacket == nullptr or packetSize == 0) {
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
store_address_t storeId;
|
store_address_t storeId;
|
||||||
ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRecvd);
|
ReturnValue_t result = tcStore->addData(&storeId, spacePacket, packetSize);
|
||||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
#if FSFW_VERBOSE_LEVEL >= 1
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::warning<< "TcpTmTcServer::handleServerOperation: Data storage failed." << std::endl;
|
sif::warning << "TcpTmTcServer::handleServerOperation: Data storage with packet size" <<
|
||||||
sif::warning << "Packet size: " << bytesRecvd << std::endl;
|
packetSize << " failed" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printWarning("TcpTmTcServer::handleServerOperation: Data storage with packet size %d "
|
||||||
|
"failed\n", packetSize);
|
||||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TmTcMessage message(storeId);
|
TmTcMessage message(storeId);
|
||||||
@ -189,8 +242,11 @@ ReturnValue_t TcpTmTcServer::handleTcReception(size_t bytesRecvd) {
|
|||||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
#if FSFW_VERBOSE_LEVEL >= 1
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::warning << "UdpTcPollingTask::handleSuccessfullTcRead: "
|
sif::warning << "TcpTmTcServer::handleServerOperation: "
|
||||||
" Sending message to queue failed" << std::endl;
|
" Sending message to queue failed" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printWarning("TcpTmTcServer::handleServerOperation: "
|
||||||
|
" Sending message to queue failed\n");
|
||||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||||
tcStore->deleteData(storeId);
|
tcStore->deleteData(storeId);
|
||||||
@ -198,21 +254,26 @@ ReturnValue_t TcpTmTcServer::handleTcReception(size_t bytesRecvd) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TcpTmTcServer::setTcpBacklog(uint8_t tcpBacklog) {
|
|
||||||
this->tcpBacklog = tcpBacklog;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string TcpTmTcServer::getTcpPort() const {
|
std::string TcpTmTcServer::getTcpPort() const {
|
||||||
return tcpPort;
|
return tcpConfig.tcpPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket) {
|
void TcpTmTcServer::setSpacePacketParsingOptions(std::vector<uint16_t> validPacketIds) {
|
||||||
|
this->validPacketIds = validPacketIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
TcpTmTcServer::TcpConfig& TcpTmTcServer::getTcpConfigStruct() {
|
||||||
|
return tcpConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket, bool& tmSent) {
|
||||||
// Access to the FIFO is mutex protected because it is filled by the bridge
|
// Access to the FIFO is mutex protected because it is filled by the bridge
|
||||||
MutexGuard(tmtcBridge->mutex, tmtcBridge->timeoutType, tmtcBridge->mutexTimeoutMs);
|
MutexGuard(tmtcBridge->mutex, tmtcBridge->timeoutType, tmtcBridge->mutexTimeoutMs);
|
||||||
store_address_t storeId;
|
store_address_t storeId;
|
||||||
while((not tmtcBridge->tmFifo->empty()) and
|
while((not tmtcBridge->tmFifo->empty()) and
|
||||||
(tmtcBridge->packetSentCounter < tmtcBridge->sentPacketsPerCycle)) {
|
(tmtcBridge->packetSentCounter < tmtcBridge->sentPacketsPerCycle)) {
|
||||||
tmtcBridge->tmFifo->retrieve(&storeId);
|
// Send can fail, so only peek from the FIFO
|
||||||
|
tmtcBridge->tmFifo->peek(&storeId);
|
||||||
|
|
||||||
// Using the store accessor will take care of deleting TM from the store automatically
|
// Using the store accessor will take care of deleting TM from the store automatically
|
||||||
ConstStorageAccessor storeAccessor(storeId);
|
ConstStorageAccessor storeAccessor(storeId);
|
||||||
@ -220,13 +281,134 @@ ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket) {
|
|||||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
if(wiretappingEnabled) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::info << "Sending TM:" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printInfo("Sending TM:\n");
|
||||||
|
#endif
|
||||||
|
arrayprinter::print(storeAccessor.data(), storeAccessor.size());
|
||||||
|
}
|
||||||
int retval = send(connSocket,
|
int retval = send(connSocket,
|
||||||
reinterpret_cast<const char*>(storeAccessor.data()),
|
reinterpret_cast<const char*>(storeAccessor.data()),
|
||||||
storeAccessor.size(),
|
storeAccessor.size(),
|
||||||
tcpTmFlags);
|
tcpConfig.tcpTmFlags);
|
||||||
if(retval != static_cast<int>(storeAccessor.size())) {
|
if(retval == static_cast<int>(storeAccessor.size())) {
|
||||||
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::SEND_CALL);
|
// Packet sent, clear FIFO entry
|
||||||
|
tmtcBridge->tmFifo->pop();
|
||||||
|
tmSent = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(retval <= 0) {
|
||||||
|
// Assume that the client has closed the connection here for now
|
||||||
|
handleSocketError(storeAccessor);
|
||||||
|
return CONN_BROKEN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReturnValue_t TcpTmTcServer::handleTcRingBufferData(size_t availableReadData) {
|
||||||
|
ReturnValue_t status = HasReturnvaluesIF::RETURN_OK;
|
||||||
|
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||||
|
size_t readAmount = availableReadData;
|
||||||
|
lastRingBufferSize = availableReadData;
|
||||||
|
if(readAmount >= ringBuffer.getMaxSize()) {
|
||||||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
// Possible configuration error, too much data or/and data coming in too fast,
|
||||||
|
// requiring larger buffers
|
||||||
|
sif::warning << "TcpTmTcServer::handleServerOperation: Ring buffer reached " <<
|
||||||
|
"fill count" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printWarning("TcpTmTcServer::handleServerOperation: Ring buffer reached "
|
||||||
|
"fill count");
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if(readAmount >= receptionBuffer.size()) {
|
||||||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
// Possible configuration error, too much data or/and data coming in too fast,
|
||||||
|
// requiring larger buffers
|
||||||
|
sif::warning << "TcpTmTcServer::handleServerOperation: "
|
||||||
|
"Reception buffer too small " << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printWarning("TcpTmTcServer::handleServerOperation: Reception buffer too small\n");
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
readAmount = receptionBuffer.size();
|
||||||
|
}
|
||||||
|
ringBuffer.readData(receptionBuffer.data(), readAmount, true);
|
||||||
|
const uint8_t* bufPtr = receptionBuffer.data();
|
||||||
|
const uint8_t** bufPtrPtr = &bufPtr;
|
||||||
|
size_t startIdx = 0;
|
||||||
|
size_t foundSize = 0;
|
||||||
|
size_t readLen = 0;
|
||||||
|
while(readLen < readAmount) {
|
||||||
|
result = spacePacketParser->parseSpacePackets(bufPtrPtr, readAmount,
|
||||||
|
startIdx, foundSize, readLen);
|
||||||
|
switch(result) {
|
||||||
|
case(SpacePacketParser::NO_PACKET_FOUND):
|
||||||
|
case(SpacePacketParser::SPLIT_PACKET): {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(HasReturnvaluesIF::RETURN_OK): {
|
||||||
|
result = handleTcReception(receptionBuffer.data() + startIdx, foundSize);
|
||||||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
status = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ringBuffer.deleteData(foundSize);
|
||||||
|
lastRingBufferSize = ringBuffer.getAvailableReadData();
|
||||||
|
std::memset(receptionBuffer.data() + startIdx, 0, foundSize);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TcpTmTcServer::enableWiretapping(bool enable) {
|
||||||
|
this->wiretappingEnabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TcpTmTcServer::handleSocketError(ConstStorageAccessor &accessor) {
|
||||||
|
// Don't delete data
|
||||||
|
accessor.release();
|
||||||
|
auto socketError = getLastSocketError();
|
||||||
|
switch(socketError) {
|
||||||
|
#if defined PLATFORM_WIN
|
||||||
|
case(WSAECONNRESET): {
|
||||||
|
// See https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send
|
||||||
|
// Remote client might have shut down connection
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
case(EPIPE): {
|
||||||
|
// See https://man7.org/linux/man-pages/man2/send.2.html
|
||||||
|
// Remote client might have shut down connection
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
default: {
|
||||||
|
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::SEND_CALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined PLATFORM_WIN
|
||||||
|
void TcpTmTcServer::setSocketNonBlocking(socket_t &connSocket) {
|
||||||
|
u_long iMode = 1;
|
||||||
|
int iResult = ioctlsocket(connSocket, FIONBIO, &iMode);
|
||||||
|
if(iResult != NO_ERROR) {
|
||||||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::warning << "TcpTmTcServer::handleServerOperation: Setting socket"
|
||||||
|
" non-blocking failed with error " << iResult;
|
||||||
|
#else
|
||||||
|
sif::printWarning("TcpTmTcServer::handleServerOperation: Setting socket"
|
||||||
|
" non-blocking failed with error %d\n", iResult);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "fsfw/platform.h"
|
#include "fsfw/platform.h"
|
||||||
#include "fsfw/osal/common/tcpipHelpers.h"
|
#include "fsfw/osal/common/tcpipHelpers.h"
|
||||||
#include "fsfw/ipc/messageQueueDefinitions.h"
|
#include "fsfw/ipc/messageQueueDefinitions.h"
|
||||||
|
#include "fsfw/container/SimpleRingBuffer.h"
|
||||||
#include "fsfw/ipc/MessageQueueIF.h"
|
#include "fsfw/ipc/MessageQueueIF.h"
|
||||||
#include "fsfw/objectmanager/frameworkObjects.h"
|
#include "fsfw/objectmanager/frameworkObjects.h"
|
||||||
#include "fsfw/objectmanager/SystemObject.h"
|
#include "fsfw/objectmanager/SystemObject.h"
|
||||||
@ -20,6 +21,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class TcpTmTcBridge;
|
class TcpTmTcBridge;
|
||||||
|
class SpacePacketParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief TCP server implementation
|
* @brief TCP server implementation
|
||||||
@ -42,9 +44,38 @@ class TcpTmTcServer:
|
|||||||
public TcpIpBase,
|
public TcpIpBase,
|
||||||
public ExecutableObjectIF {
|
public ExecutableObjectIF {
|
||||||
public:
|
public:
|
||||||
|
enum class ReceptionModes {
|
||||||
|
SPACE_PACKETS
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TcpConfig {
|
||||||
|
public:
|
||||||
|
TcpConfig(std::string tcpPort): tcpPort(tcpPort) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passed to the recv call
|
||||||
|
*/
|
||||||
|
int tcpFlags = 0;
|
||||||
|
int tcpBacklog = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If no telecommands packets are being received and no telemetry is being sent,
|
||||||
|
* the TCP server will delay periodically by this amount to decrease the CPU load
|
||||||
|
*/
|
||||||
|
uint32_t tcpLoopDelay = DEFAULT_LOOP_DELAY_MS ;
|
||||||
|
/**
|
||||||
|
* Passed to the send call
|
||||||
|
*/
|
||||||
|
int tcpTmFlags = 0;
|
||||||
|
|
||||||
|
const std::string tcpPort;
|
||||||
|
};
|
||||||
|
|
||||||
static const std::string DEFAULT_SERVER_PORT;
|
static const std::string DEFAULT_SERVER_PORT;
|
||||||
|
|
||||||
static constexpr size_t ETHERNET_MTU_SIZE = 1500;
|
static constexpr size_t ETHERNET_MTU_SIZE = 1500;
|
||||||
|
static constexpr size_t RING_BUFFER_SIZE = ETHERNET_MTU_SIZE * 3;
|
||||||
|
static constexpr uint32_t DEFAULT_LOOP_DELAY_MS = 200;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TCP Server Constructor
|
* TCP Server Constructor
|
||||||
@ -55,11 +86,21 @@ public:
|
|||||||
* @param customTcpServerPort The user can specify another port than the default (7301) here.
|
* @param customTcpServerPort The user can specify another port than the default (7301) here.
|
||||||
*/
|
*/
|
||||||
TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge,
|
TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge,
|
||||||
size_t receptionBufferSize = ETHERNET_MTU_SIZE + 1,
|
size_t receptionBufferSize = RING_BUFFER_SIZE,
|
||||||
std::string customTcpServerPort = "");
|
size_t ringBufferSize = RING_BUFFER_SIZE,
|
||||||
|
std::string customTcpServerPort = DEFAULT_SERVER_PORT,
|
||||||
|
ReceptionModes receptionMode = ReceptionModes::SPACE_PACKETS);
|
||||||
virtual~ TcpTmTcServer();
|
virtual~ TcpTmTcServer();
|
||||||
|
|
||||||
void setTcpBacklog(uint8_t tcpBacklog);
|
void enableWiretapping(bool enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a handle to the TCP configuration struct, which can be used to configure TCP
|
||||||
|
* properties
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
TcpConfig& getTcpConfigStruct();
|
||||||
|
void setSpacePacketParsingOptions(std::vector<uint16_t> validPacketIds);
|
||||||
|
|
||||||
ReturnValue_t initialize() override;
|
ReturnValue_t initialize() override;
|
||||||
ReturnValue_t performOperation(uint8_t opCode) override;
|
ReturnValue_t performOperation(uint8_t opCode) override;
|
||||||
@ -71,25 +112,33 @@ protected:
|
|||||||
StorageManagerIF* tcStore = nullptr;
|
StorageManagerIF* tcStore = nullptr;
|
||||||
StorageManagerIF* tmStore = nullptr;
|
StorageManagerIF* tmStore = nullptr;
|
||||||
private:
|
private:
|
||||||
|
static constexpr ReturnValue_t CONN_BROKEN = HasReturnvaluesIF::makeReturnCode(1, 0);
|
||||||
//! TMTC bridge is cached.
|
//! TMTC bridge is cached.
|
||||||
object_id_t tmtcBridgeId = objects::NO_OBJECT;
|
object_id_t tmtcBridgeId = objects::NO_OBJECT;
|
||||||
TcpTmTcBridge* tmtcBridge = nullptr;
|
TcpTmTcBridge* tmtcBridge = nullptr;
|
||||||
|
bool wiretappingEnabled = false;
|
||||||
|
|
||||||
std::string tcpPort;
|
ReceptionModes receptionMode;
|
||||||
int tcpFlags = 0;
|
TcpConfig tcpConfig;
|
||||||
socket_t listenerTcpSocket = 0;
|
|
||||||
struct sockaddr tcpAddress;
|
struct sockaddr tcpAddress;
|
||||||
|
socket_t listenerTcpSocket = 0;
|
||||||
|
|
||||||
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
|
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
|
||||||
int tcpAddrLen = sizeof(tcpAddress);
|
|
||||||
int tcpBacklog = 3;
|
|
||||||
|
|
||||||
std::vector<uint8_t> receptionBuffer;
|
std::vector<uint8_t> receptionBuffer;
|
||||||
int tcpSockOpt = 0;
|
SimpleRingBuffer ringBuffer;
|
||||||
int tcpTmFlags = 0;
|
std::vector<uint16_t> validPacketIds;
|
||||||
|
SpacePacketParser* spacePacketParser = nullptr;
|
||||||
|
uint8_t lastRingBufferSize = 0;
|
||||||
|
|
||||||
void handleServerOperation(socket_t connSocket);
|
virtual void handleServerOperation(socket_t& connSocket);
|
||||||
ReturnValue_t handleTcReception(size_t bytesRecvd);
|
ReturnValue_t handleTcReception(uint8_t* spacePacket, size_t packetSize);
|
||||||
ReturnValue_t handleTmSending(socket_t connSocket);
|
ReturnValue_t handleTmSending(socket_t connSocket, bool& tmSent);
|
||||||
|
ReturnValue_t handleTcRingBufferData(size_t availableReadData);
|
||||||
|
void handleSocketError(ConstStorageAccessor& accessor);
|
||||||
|
#if defined PLATFORM_WIN
|
||||||
|
void setSocketNonBlocking(socket_t& connSocket);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* FSFW_OSAL_COMMON_TCP_TMTC_SERVER_H_ */
|
#endif /* FSFW_OSAL_COMMON_TCP_TMTC_SERVER_H_ */
|
||||||
|
@ -62,8 +62,13 @@ ReturnValue_t CommandExecutor::close() {
|
|||||||
|
|
||||||
void CommandExecutor::printLastError(std::string funcName) const {
|
void CommandExecutor::printLastError(std::string funcName) const {
|
||||||
if(lastError != 0) {
|
if(lastError != 0) {
|
||||||
sif::error << funcName << " pclose failed with code " <<
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
lastError << ": " << strerror(lastError) << std::endl;
|
sif::warning << funcName << " pclose failed with code " << lastError << ": " <<
|
||||||
|
strerror(lastError) << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printError("%s pclose failed with code %d: %s\n",
|
||||||
|
funcName, lastError, strerror(lastError));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,15 +103,23 @@ ReturnValue_t CommandExecutor::check(bool& bytesRead) {
|
|||||||
ssize_t readBytes = read(currentFd, readVec.data(), readVec.size());
|
ssize_t readBytes = read(currentFd, readVec.data(), readVec.size());
|
||||||
if(readBytes == 0) {
|
if(readBytes == 0) {
|
||||||
// Should not happen
|
// Should not happen
|
||||||
sif::warning << "CommandExecutor::check: "
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
"No bytes read after poll event.." << std::endl;
|
sif::warning << "CommandExecutor::check: No bytes read "
|
||||||
|
"after poll event.." << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printWarning("CommandExecutor::check: No bytes read after poll event..\n");
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if(readBytes > 0) {
|
else if(readBytes > 0) {
|
||||||
bytesRead = true;
|
bytesRead = true;
|
||||||
if(printOutput) {
|
if(printOutput) {
|
||||||
// It is assumed the command output is line terminated
|
// It is assumed the command output is line terminated
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::info << currentCmd << " | " << readVec.data();
|
sif::info << currentCmd << " | " << readVec.data();
|
||||||
|
#else
|
||||||
|
sif::printInfo("%s | %s", currentCmd, readVec.data());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if(ringBuffer != nullptr) {
|
if(ringBuffer != nullptr) {
|
||||||
ringBuffer->writeData(reinterpret_cast<const uint8_t*>(
|
ringBuffer->writeData(reinterpret_cast<const uint8_t*>(
|
||||||
@ -121,12 +134,20 @@ ReturnValue_t CommandExecutor::check(bool& bytesRead) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Should also not happen
|
// Should also not happen
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::warning << "CommandExecutor::check: Error " << errno << ": " <<
|
sif::warning << "CommandExecutor::check: Error " << errno << ": " <<
|
||||||
strerror(errno) << std::endl;
|
strerror(errno) << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printWarning("CommandExecutor::check: Error %d: %s\n", errno, strerror(errno));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(waiter.revents & POLLERR) {
|
else if(waiter.revents & POLLERR) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::warning << "CommandExecuter::check: Poll error" << std::endl;
|
sif::warning << "CommandExecuter::check: Poll error" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printWarning("CommandExecuter::check: Poll error\n");
|
||||||
|
#endif
|
||||||
return COMMAND_ERROR;
|
return COMMAND_ERROR;
|
||||||
}
|
}
|
||||||
else if(waiter.revents & POLLHUP) {
|
else if(waiter.revents & POLLHUP) {
|
||||||
@ -165,7 +186,11 @@ ReturnValue_t CommandExecutor::executeBlocking() {
|
|||||||
while(fgets(readVec.data(), readVec.size(), currentCmdFile) != nullptr) {
|
while(fgets(readVec.data(), readVec.size(), currentCmdFile) != nullptr) {
|
||||||
std::string output(readVec.data());
|
std::string output(readVec.data());
|
||||||
if(printOutput) {
|
if(printOutput) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::info << currentCmd << " | " << output;
|
sif::info << currentCmd << " | " << output;
|
||||||
|
#else
|
||||||
|
sif::printInfo("%s | %s", currentCmd, output);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if(ringBuffer != nullptr) {
|
if(ringBuffer != nullptr) {
|
||||||
ringBuffer->writeData(reinterpret_cast<const uint8_t*>(output.data()), output.size());
|
ringBuffer->writeData(reinterpret_cast<const uint8_t*>(output.data()), output.size());
|
||||||
|
@ -80,6 +80,7 @@ enum: uint8_t {
|
|||||||
FIXED_SLOT_TASK_IF, //FTIF
|
FIXED_SLOT_TASK_IF, //FTIF
|
||||||
MGM_LIS3MDL, //MGMLIS3
|
MGM_LIS3MDL, //MGMLIS3
|
||||||
MGM_RM3100, //MGMRM3100
|
MGM_RM3100, //MGMRM3100
|
||||||
|
SPACE_PACKET_PARSER, //SPPA
|
||||||
FW_CLASS_ID_COUNT // [EXPORT] : [END]
|
FW_CLASS_ID_COUNT // [EXPORT] : [END]
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -29,12 +29,31 @@ PUSDistributor::TcMqMapIter PUSDistributor::selectDestination() {
|
|||||||
tcStatus = checker.checkPacket(currentPacket);
|
tcStatus = checker.checkPacket(currentPacket);
|
||||||
if(tcStatus != HasReturnvaluesIF::RETURN_OK) {
|
if(tcStatus != HasReturnvaluesIF::RETURN_OK) {
|
||||||
#if FSFW_VERBOSE_LEVEL >= 1
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
std::string keyword;
|
||||||
|
if(tcStatus == TcPacketCheck::INCORRECT_CHECKSUM) {
|
||||||
|
keyword = "checksum";
|
||||||
|
}
|
||||||
|
else if(tcStatus == TcPacketCheck::INCORRECT_PRIMARY_HEADER) {
|
||||||
|
keyword = "incorrect primary header";
|
||||||
|
}
|
||||||
|
else if(tcStatus == TcPacketCheck::ILLEGAL_APID) {
|
||||||
|
keyword = "illegal APID";
|
||||||
|
}
|
||||||
|
else if(tcStatus == TcPacketCheck::INCORRECT_SECONDARY_HEADER) {
|
||||||
|
keyword = "incorrect secondary header";
|
||||||
|
}
|
||||||
|
else if(tcStatus == TcPacketCheck::INCOMPLETE_PACKET) {
|
||||||
|
keyword = "incomplete packet";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
keyword = "unnamed error";
|
||||||
|
}
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::debug << "PUSDistributor::handlePacket: Packet format invalid, code " <<
|
sif::warning << "PUSDistributor::handlePacket: Packet format invalid, "
|
||||||
static_cast<int>(tcStatus) << std::endl;
|
<< keyword << " error" << std::endl;
|
||||||
#else
|
#else
|
||||||
sif::printDebug("PUSDistributor::handlePacket: Packet format invalid, code %d\n",
|
sif::printWarning("PUSDistributor::handlePacket: Packet format invalid, "
|
||||||
static_cast<int>(tcStatus));
|
"%s error\n", keyword);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
* The constructor initializes the packet and sets all header information
|
* The constructor initializes the packet and sets all header information
|
||||||
* according to the passed parameters.
|
* according to the passed parameters.
|
||||||
* @param packetDataLength Sets the packet data length field and therefore specifies the size of the packet.
|
* @param packetDataLength Sets the packet data length field and therefore specifies
|
||||||
|
* the size of the packet.
|
||||||
* @param isTelecommand Sets the packet type field to either TC (true) or TM (false).
|
* @param isTelecommand Sets the packet type field to either TC (true) or TM (false).
|
||||||
* @param apid Sets the packet's APID field. The default value describes an idle packet.
|
* @param apid Sets the packet's APID field. The default value describes an idle packet.
|
||||||
* @param sequenceCount ets the packet's Source Sequence Count field.
|
* @param sequenceCount ets the packet's Source Sequence Count field.
|
||||||
@ -30,6 +31,7 @@ public:
|
|||||||
* The class's default destructor.
|
* The class's default destructor.
|
||||||
*/
|
*/
|
||||||
virtual ~SpacePacket();
|
virtual ~SpacePacket();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* With this call, the complete data content (including the CCSDS Primary
|
* With this call, the complete data content (including the CCSDS Primary
|
||||||
* Header) is overwritten with the byte stream given.
|
* Header) is overwritten with the byte stream given.
|
||||||
@ -67,4 +69,16 @@ protected:
|
|||||||
SpacePacketData localData;
|
SpacePacketData localData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace spacepacket {
|
||||||
|
|
||||||
|
constexpr uint16_t getTcSpacePacketIdFromApid(uint16_t apid) {
|
||||||
|
return (0x18 << 8) | (((apid >> 8) & 0x07) << 8) | (apid & 0x00ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint16_t getTmSpacePacketIdFromApid(uint16_t apid) {
|
||||||
|
return (0x08 << 8) | (((apid >> 8) & 0x07) << 8) | (apid & 0x00ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* SPACEPACKET_H_ */
|
#endif /* SPACEPACKET_H_ */
|
||||||
|
@ -60,8 +60,9 @@ uint16_t SpacePacketBase::getAPID( void ) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SpacePacketBase::setAPID( uint16_t new_apid ) {
|
void SpacePacketBase::setAPID( uint16_t new_apid ) {
|
||||||
//Use first three bits of new APID, but keep rest of packet id as it was (see specification).
|
// Use first three bits of new APID, but keep rest of packet id as it was (see specification).
|
||||||
this->data->header.packet_id_h = (this->data->header.packet_id_h & 0b11111000) | ( ( new_apid & 0x0700 ) >> 8 );
|
this->data->header.packet_id_h = (this->data->header.packet_id_h & 0b11111000) |
|
||||||
|
( ( new_apid & 0x0700 ) >> 8 );
|
||||||
this->data->header.packet_id_l = ( new_apid & 0x00FF );
|
this->data->header.packet_id_l = ( new_apid & 0x00FF );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +86,8 @@ uint16_t SpacePacketBase::getPacketSequenceCount( void ) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SpacePacketBase::setPacketSequenceCount( uint16_t new_count) {
|
void SpacePacketBase::setPacketSequenceCount( uint16_t new_count) {
|
||||||
this->data->header.sequence_control_h = ( this->data->header.sequence_control_h & 0b11000000 ) | ( ( (new_count%LIMIT_SEQUENCE_COUNT) & 0x3F00 ) >> 8 );
|
this->data->header.sequence_control_h = ( this->data->header.sequence_control_h & 0b11000000 ) |
|
||||||
|
( ( (new_count%LIMIT_SEQUENCE_COUNT) & 0x3F00 ) >> 8 );
|
||||||
this->data->header.sequence_control_l = ( (new_count%LIMIT_SEQUENCE_COUNT) & 0x00FF );
|
this->data->header.sequence_control_l = ( (new_count%LIMIT_SEQUENCE_COUNT) & 0x00FF );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +102,7 @@ void SpacePacketBase::setPacketDataLength( uint16_t new_length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t SpacePacketBase::getFullSize() {
|
size_t SpacePacketBase::getFullSize() {
|
||||||
//+1 is done because size in packet data length field is: size of data field -1
|
// +1 is done because size in packet data length field is: size of data field -1
|
||||||
return this->getPacketDataLength() + sizeof(this->data->header) + 1;
|
return this->getPacketDataLength() + sizeof(this->data->header) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual ~SpacePacketBase();
|
virtual ~SpacePacketBase();
|
||||||
|
|
||||||
//CCSDS Methods:
|
//CCSDS Methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter for the packet version number field.
|
* Getter for the packet version number field.
|
||||||
* @return Returns the highest three bit of the packet in one byte.
|
* @return Returns the highest three bit of the packet in one byte.
|
||||||
@ -162,7 +163,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setPacketDataLength( uint16_t setLength );
|
void setPacketDataLength( uint16_t setLength );
|
||||||
|
|
||||||
//Helper methods:
|
// Helper methods
|
||||||
/**
|
/**
|
||||||
* This method returns a raw uint8_t pointer to the packet.
|
* This method returns a raw uint8_t pointer to the packet.
|
||||||
* @return A \c uint8_t pointer to the first byte of the CCSDS primary header.
|
* @return A \c uint8_t pointer to the first byte of the CCSDS primary header.
|
||||||
|
@ -77,9 +77,9 @@ TmPacketStoredPusC::TmPacketStoredPusC(uint16_t apid, uint8_t service,
|
|||||||
"%d too large\n", sizeToReserve);
|
"%d too large\n", sizeToReserve);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
TmPacketStoredBase::checkAndReportLostTm();
|
TmPacketStoredBase::checkAndReportLostTm();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -6,4 +6,5 @@ target_sources(${LIB_FSFW_NAME}
|
|||||||
TmTcBridge.cpp
|
TmTcBridge.cpp
|
||||||
TmTcMessage.cpp
|
TmTcMessage.cpp
|
||||||
VerificationReporter.cpp
|
VerificationReporter.cpp
|
||||||
|
SpacePacketParser.cpp
|
||||||
)
|
)
|
77
src/fsfw/tmtcservices/SpacePacketParser.cpp
Normal file
77
src/fsfw/tmtcservices/SpacePacketParser.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#include <fsfw/serviceinterface/ServiceInterface.h>
|
||||||
|
#include <fsfw/tmtcservices/SpacePacketParser.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
SpacePacketParser::SpacePacketParser(std::vector<uint16_t> validPacketIds):
|
||||||
|
validPacketIds(validPacketIds) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t SpacePacketParser::parseSpacePackets(const uint8_t *buffer,
|
||||||
|
const size_t maxSize, size_t& startIndex, size_t& foundSize) {
|
||||||
|
const uint8_t** tempPtr = &buffer;
|
||||||
|
size_t readLen = 0;
|
||||||
|
return parseSpacePackets(tempPtr, maxSize, startIndex, foundSize, readLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t SpacePacketParser::parseSpacePackets(const uint8_t **buffer, const size_t maxSize,
|
||||||
|
size_t &startIndex, size_t &foundSize, size_t& readLen) {
|
||||||
|
if(buffer == nullptr or maxSize < 5) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::warning << "SpacePacketParser::parseSpacePackets: Frame invalid" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printWarning("SpacePacketParser::parseSpacePackets: Frame invalid\n");
|
||||||
|
#endif
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
const uint8_t* bufPtr = *buffer;
|
||||||
|
|
||||||
|
auto verifyLengthField = [&](size_t idx) {
|
||||||
|
uint16_t lengthField = bufPtr[idx + 4] << 8 | bufPtr[idx + 5];
|
||||||
|
size_t packetSize = lengthField + 7;
|
||||||
|
startIndex = idx;
|
||||||
|
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||||
|
if(lengthField == 0) {
|
||||||
|
// Skip whole header for now
|
||||||
|
foundSize = 6;
|
||||||
|
result = NO_PACKET_FOUND;
|
||||||
|
}
|
||||||
|
else if(packetSize + idx > maxSize) {
|
||||||
|
// Don't increment buffer and read length here, user has to decide what to do
|
||||||
|
foundSize = packetSize;
|
||||||
|
return SPLIT_PACKET;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foundSize = packetSize;
|
||||||
|
}
|
||||||
|
*buffer += foundSize;
|
||||||
|
readLen += idx + foundSize;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t idx = 0;
|
||||||
|
// Space packet ID as start marker
|
||||||
|
if(validPacketIds.size() > 0) {
|
||||||
|
while(idx < maxSize - 5) {
|
||||||
|
uint16_t currentPacketId = bufPtr[idx] << 8 | bufPtr[idx + 1];
|
||||||
|
if(std::find(validPacketIds.begin(), validPacketIds.end(), currentPacketId) !=
|
||||||
|
validPacketIds.end()) {
|
||||||
|
if(idx + 5 >= maxSize) {
|
||||||
|
return SPLIT_PACKET;
|
||||||
|
}
|
||||||
|
return verifyLengthField(idx);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startIndex = 0;
|
||||||
|
foundSize = maxSize;
|
||||||
|
*buffer += foundSize;
|
||||||
|
readLen += foundSize;
|
||||||
|
return NO_PACKET_FOUND;
|
||||||
|
}
|
||||||
|
// Assume that the user verified a valid start of a space packet
|
||||||
|
else {
|
||||||
|
return verifyLengthField(idx);
|
||||||
|
}
|
||||||
|
}
|
78
src/fsfw/tmtcservices/SpacePacketParser.h
Normal file
78
src/fsfw/tmtcservices/SpacePacketParser.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#ifndef FRAMEWORK_TMTCSERVICES_PUSPARSER_H_
|
||||||
|
#define FRAMEWORK_TMTCSERVICES_PUSPARSER_H_
|
||||||
|
|
||||||
|
#include "fsfw/container/DynamicFIFO.h"
|
||||||
|
#include "fsfw/returnvalues/FwClassIds.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This small helper class scans a given buffer for space packets.
|
||||||
|
* Can be used if space packets are serialized in a tightly packed frame.
|
||||||
|
* @details
|
||||||
|
* The parser uses the length field field and the 16-bit TC packet ID of the space packets to find
|
||||||
|
* find space packets in a given data stream
|
||||||
|
* @author R. Mueller
|
||||||
|
*/
|
||||||
|
class SpacePacketParser {
|
||||||
|
public:
|
||||||
|
//! The first entry is the index inside the buffer while the second index
|
||||||
|
//! is the size of the PUS packet starting at that index.
|
||||||
|
using IndexSizePair = std::pair<size_t, size_t>;
|
||||||
|
|
||||||
|
static constexpr uint8_t INTERFACE_ID = CLASS_ID::SPACE_PACKET_PARSER;
|
||||||
|
static constexpr ReturnValue_t NO_PACKET_FOUND = MAKE_RETURN_CODE(0x00);
|
||||||
|
static constexpr ReturnValue_t SPLIT_PACKET = MAKE_RETURN_CODE(0x01);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parser constructor.
|
||||||
|
* @param validPacketIds This vector contains the allowed 16-bit TC packet ID start markers
|
||||||
|
* The parser will search for these stark markers to detect the start of a space packet.
|
||||||
|
* It is also possible to pass an empty vector here, but this is not recommended.
|
||||||
|
* If an empty vector is passed, the parser will assume that the start of the given stream
|
||||||
|
* contains the start of a new space packet.
|
||||||
|
*/
|
||||||
|
SpacePacketParser(std::vector<uint16_t> validPacketIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a given frame for space packets but also increment the given buffer and assign the
|
||||||
|
* total number of bytes read so far
|
||||||
|
* @param buffer Parser will look for space packets in this buffer
|
||||||
|
* @param maxSize Maximum size of the buffer
|
||||||
|
* @param startIndex Start index of a found space packet
|
||||||
|
* @param foundSize Found size of the space packet
|
||||||
|
* @param readLen Length read so far. This value is incremented by the number of parsed
|
||||||
|
* bytes which also includes the size of a found packet
|
||||||
|
* -@c NO_PACKET_FOUND if no packet was found in the given buffer or the length field is
|
||||||
|
* invalid. foundSize will be set to the size of the space packet header. buffer and
|
||||||
|
* readLen will be incremented accordingly.
|
||||||
|
* -@c SPLIT_PACKET if a packet was found but the detected size exceeds maxSize. foundSize
|
||||||
|
* will be set to the detected packet size and startIndex will be set to the start of the
|
||||||
|
* detected packet. buffer and read length will not be incremented but the found length
|
||||||
|
* will be assigned.
|
||||||
|
* -@c RETURN_OK if a packet was found
|
||||||
|
*/
|
||||||
|
ReturnValue_t parseSpacePackets(const uint8_t **buffer, const size_t maxSize,
|
||||||
|
size_t& startIndex, size_t& foundSize, size_t& readLen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a given frame for space packets
|
||||||
|
* @param buffer Parser will look for space packets in this buffer
|
||||||
|
* @param maxSize Maximum size of the buffer
|
||||||
|
* @param startIndex Start index of a found space packet
|
||||||
|
* @param foundSize Found size of the space packet
|
||||||
|
* -@c NO_PACKET_FOUND if no packet was found in the given buffer or the length field is
|
||||||
|
* invalid. foundSize will be set to the size of the space packet header
|
||||||
|
* -@c SPLIT_PACKET if a packet was found but the detected size exceeds maxSize. foundSize
|
||||||
|
* will be set to the detected packet size and startIndex will be set to the start of the
|
||||||
|
* detected packet
|
||||||
|
* -@c RETURN_OK if a packet was found
|
||||||
|
*/
|
||||||
|
ReturnValue_t parseSpacePackets(const uint8_t* buffer, const size_t maxSize,
|
||||||
|
size_t& startIndex, size_t& foundSize);
|
||||||
|
private:
|
||||||
|
std::vector<uint16_t> validPacketIds;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* FRAMEWORK_TMTCSERVICES_PUSPARSER_H_ */
|
@ -1,11 +1,9 @@
|
|||||||
target_sources(${TARGET_NAME}
|
target_sources(${TARGET_NAME} PRIVATE
|
||||||
PRIVATE
|
|
||||||
ipc/MissionMessageTypes.cpp
|
ipc/MissionMessageTypes.cpp
|
||||||
pollingsequence/PollingSequenceFactory.cpp
|
pollingsequence/PollingSequenceFactory.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add include paths for the executable
|
# Add include paths for the executable
|
||||||
target_include_directories(${TARGET_NAME}
|
target_include_directories(${TARGET_NAME} PUBLIC
|
||||||
PUBLIC
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
)
|
)
|
Loading…
Reference in New Issue
Block a user