Robin Mueller
2f25ac8e7d
All checks were successful
EIVE/eive-obsw/pipeline/pr-cfdp-source-handler This commit looks good
254 lines
9.9 KiB
C++
254 lines
9.9 KiB
C++
#include "CfdpHandler.h"
|
|
|
|
#include <fsfw/cfdp/CfdpMessage.h>
|
|
#include <fsfw/ipc/CommandMessage.h>
|
|
|
|
#include "eive/definitions.h"
|
|
#include "fsfw/cfdp/pdu/AckPduReader.h"
|
|
#include "fsfw/cfdp/pdu/PduHeaderReader.h"
|
|
#include "fsfw/globalfunctions/arrayprinter.h"
|
|
#include "fsfw/ipc/QueueFactory.h"
|
|
#include "fsfw/tasks/TaskFactory.h"
|
|
#include "fsfw/tmtcservices/TmTcMessage.h"
|
|
#include "mission/sysDefs.h"
|
|
|
|
using namespace returnvalue;
|
|
using namespace cfdp;
|
|
|
|
CfdpHandler::CfdpHandler(const FsfwHandlerParams& fsfwHandlerParams, const CfdpHandlerCfg& cfdpCfg,
|
|
const std::atomic_bool& throttleSignal)
|
|
: SystemObject(fsfwHandlerParams.objectId),
|
|
pduQueue(fsfwHandlerParams.tmtcQueue),
|
|
cfdpRequestQueue(fsfwHandlerParams.cfdpQueue),
|
|
localCfg(cfdpCfg.id, cfdpCfg.indicCfg, cfdpCfg.faultHandler),
|
|
remoteCfgProvider(cfdpCfg.remoteCfgProvider),
|
|
fsfwParams(fsfwHandlerParams.packetDest, &fsfwHandlerParams.tmtcQueue, this,
|
|
fsfwHandlerParams.tcStore, fsfwHandlerParams.tmStore),
|
|
destHandler(DestHandlerParams(localCfg, cfdpCfg.userHandler, cfdpCfg.remoteCfgProvider,
|
|
cfdpCfg.packetInfoList, cfdpCfg.lostSegmentsList),
|
|
this->fsfwParams),
|
|
srcHandler(SourceHandlerParams(localCfg, cfdpCfg.userHandler, seqCntProvider),
|
|
this->fsfwParams),
|
|
ipcStore(fsfwHandlerParams.ipcStore),
|
|
throttleSignal(throttleSignal) {}
|
|
|
|
[[nodiscard]] const char* CfdpHandler::getName() const { return "CFDP Handler"; }
|
|
|
|
[[nodiscard]] uint32_t CfdpHandler::getIdentifier() const {
|
|
return destHandler.getDestHandlerParams().cfg.localId.getValue();
|
|
}
|
|
|
|
[[nodiscard]] MessageQueueId_t CfdpHandler::getRequestQueue() const { return pduQueue.getId(); }
|
|
|
|
ReturnValue_t CfdpHandler::initialize() {
|
|
ReturnValue_t result = destHandler.initialize();
|
|
if (result != OK) {
|
|
return result;
|
|
}
|
|
tcStore = destHandler.getTcStore();
|
|
tmStore = destHandler.getTmStore();
|
|
|
|
return SystemObject::initialize();
|
|
}
|
|
|
|
[[noreturn]] ReturnValue_t CfdpHandler::performOperation(uint8_t operationCode) {
|
|
while (true) {
|
|
bool shortDelay = false;
|
|
ReturnValue_t result = handlePduPacketMessages();
|
|
if (result != OK) {
|
|
}
|
|
result = handleCfdpMessages();
|
|
if (result != OK) {
|
|
}
|
|
uint32_t fsmCount = 1;
|
|
const DestHandler::FsmResult& destResult = destHandler.stateMachine();
|
|
while (destResult.callStatus == CallStatus::CALL_AGAIN) {
|
|
if (fsmCount == config::CFDP_MAX_FSM_CALL_COUNT_DEST_HANDLER) {
|
|
shortDelay = true;
|
|
break;
|
|
}
|
|
destHandler.stateMachine();
|
|
fsmCount++;
|
|
}
|
|
fsmCount = 1;
|
|
|
|
throttlePeriodOngoing = throttleSignal;
|
|
|
|
// CFDP can be throttled by the slowest live TM handler to handle back pressure in a sensible
|
|
// way without requiring huge amounts of memory for large files.
|
|
if (!throttlePeriodOngoing) {
|
|
const SourceHandler::FsmResult& srcResult = srcHandler.stateMachine();
|
|
if (srcResult.packetsSent > 0) {
|
|
signals::CFDP_MSG_COUNTER.fetch_add(srcResult.packetsSent, std::memory_order_relaxed);
|
|
}
|
|
while (srcResult.callStatus == CallStatus::CALL_AGAIN) {
|
|
// Limit number of messages.
|
|
if (fsmCount == config::CFDP_MAX_FSM_CALL_COUNT_SRC_HANDLER) {
|
|
shortDelay = true;
|
|
break;
|
|
}
|
|
srcHandler.stateMachine();
|
|
if (srcResult.packetsSent > 0) {
|
|
signals::CFDP_MSG_COUNTER.fetch_add(srcResult.packetsSent, std::memory_order_relaxed);
|
|
}
|
|
if (srcResult.result == cfdp::TM_STORE_FULL) {
|
|
sif::warning << "CFDP Source Handler: TM store is full" << std::endl;
|
|
} else if (srcResult.result == cfdp::TARGET_MSG_QUEUE_FULL) {
|
|
sif::warning << "CFDP Source Handler: TM queue is full" << std::endl;
|
|
}
|
|
fsmCount++;
|
|
}
|
|
}
|
|
if (shortDelay) {
|
|
TaskFactory::delayTask(config::CFDP_SHORT_DELAY_MS);
|
|
continue;
|
|
}
|
|
TaskFactory::delayTask(config::CFDP_REGULAR_DELAY_MS);
|
|
}
|
|
}
|
|
|
|
ReturnValue_t CfdpHandler::handlePduPacket(TmTcMessage& msg) {
|
|
auto accessorPair = tcStore->getData(msg.getStorageId());
|
|
if (accessorPair.first != OK) {
|
|
return accessorPair.first;
|
|
}
|
|
PduHeaderReader reader(accessorPair.second.data(), accessorPair.second.size());
|
|
ReturnValue_t result = reader.parseData();
|
|
if (result != returnvalue::OK) {
|
|
return INVALID_PDU_FORMAT;
|
|
}
|
|
// The CFDP distributor should have taken care of ensuring the destination ID is correct
|
|
PduType type = reader.getPduType();
|
|
// Only the destination handler can process these PDUs
|
|
if (type == PduType::FILE_DATA) {
|
|
// Disable auto-deletion of packet
|
|
accessorPair.second.release();
|
|
PacketInfo info(type, msg.getStorageId());
|
|
result = destHandler.passPacket(info);
|
|
} else {
|
|
// Route depending on PDU type and directive type if applicable. It retrieves directive type
|
|
// from the raw stream for better performance (with sanity and directive code check).
|
|
// The routing is based on section 4.5 of the CFDP standard which specifies the PDU forwarding
|
|
// procedure.
|
|
|
|
// PDU header only. Invalid supplied data. A directive packet should have a valid data field
|
|
// with at least one byte being the directive code
|
|
const uint8_t* pduDataField = reader.getPduDataField();
|
|
if (pduDataField == nullptr) {
|
|
return INVALID_PDU_FORMAT;
|
|
}
|
|
if (not FileDirectiveReader::checkFileDirective(pduDataField[0])) {
|
|
sif::error << "CfdpHandler: Invalid PDU directive field " << static_cast<int>(pduDataField[0])
|
|
<< std::endl;
|
|
return INVALID_DIRECTIVE_FIELD;
|
|
}
|
|
auto directive = static_cast<FileDirective>(pduDataField[0]);
|
|
|
|
auto passToDestHandler = [&]() {
|
|
accessorPair.second.release();
|
|
PacketInfo info(type, msg.getStorageId(), directive);
|
|
return destHandler.passPacket(info);
|
|
};
|
|
auto passToSourceHandler = [&]() {
|
|
accessorPair.second.release();
|
|
PacketInfo info(type, msg.getStorageId(), directive);
|
|
// Implement this function.
|
|
// result = srcHandler.passPacket(info);
|
|
};
|
|
if (directive == FileDirective::METADATA or directive == FileDirective::EOF_DIRECTIVE or
|
|
directive == FileDirective::PROMPT) {
|
|
// Section b) of 4.5.3: These PDUs should always be targeted towards the file receiver a.k.a.
|
|
// the destination handler
|
|
return passToDestHandler();
|
|
} else if (directive == FileDirective::FINISH or directive == FileDirective::NAK or
|
|
directive == FileDirective::KEEP_ALIVE) {
|
|
// Section c) of 4.5.3: These PDUs should always be targeted towards the file sender a.k.a.
|
|
// the source handler
|
|
passToSourceHandler();
|
|
} else if (directive == FileDirective::ACK) {
|
|
// Section a): Recipient depends of the type of PDU that is being acknowledged. We can simply
|
|
// extract the PDU type from the raw stream. If it is an EOF PDU, this packet is passed to
|
|
// the source handler, for a Finished PDU, it is passed to the destination handler.
|
|
FileDirective ackedDirective;
|
|
if (not AckPduReader::checkAckedDirectiveField(pduDataField[1], ackedDirective)) {
|
|
return INVALID_ACK_DIRECTIVE_FIELDS;
|
|
}
|
|
if (ackedDirective == FileDirective::EOF_DIRECTIVE) {
|
|
passToSourceHandler();
|
|
} else if (ackedDirective == FileDirective::FINISH) {
|
|
return passToDestHandler();
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ReturnValue_t CfdpHandler::handleCfdpRequest(CommandMessage& msg) {
|
|
if (msg.getCommand() == CfdpMessage::PUT_REQUEST) {
|
|
sif::info << "Received CFDP put request" << std::endl;
|
|
if (srcHandler.getState() != CfdpState::IDLE) {
|
|
if (putRequestQueue.full()) {
|
|
// TODO: Trigger event and discard request. Queue is full, too many requests.
|
|
return FAILED;
|
|
}
|
|
putRequestQueue.push(CfdpMessage::getStoreId(&msg));
|
|
} else {
|
|
PutRequest putRequest;
|
|
auto accessorPair = ipcStore.getData(CfdpMessage::getStoreId(&msg));
|
|
const uint8_t* dataPtr = accessorPair.second.data();
|
|
size_t dataSize = accessorPair.second.size();
|
|
ReturnValue_t result =
|
|
putRequest.deSerialize(&dataPtr, &dataSize, SerializeIF::Endianness::MACHINE);
|
|
if (result != OK) {
|
|
return result;
|
|
}
|
|
RemoteEntityCfg* remoteCfg;
|
|
remoteCfgProvider.getRemoteCfg(putRequest.getDestId(), &remoteCfg);
|
|
if (remoteCfg == nullptr) {
|
|
sif::error << "CfdpHandler: No remote configuration found for destination ID "
|
|
<< putRequest.getDestId() << std::endl;
|
|
// TODO: Trigger event
|
|
return FAILED;
|
|
}
|
|
sif::info << "Starting file copy operation for source file "
|
|
<< putRequest.getSourceName().getString() << " and dest file "
|
|
<< putRequest.getDestName().getString() << std::endl;
|
|
return srcHandler.transactionStart(putRequest, *remoteCfg);
|
|
}
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
ReturnValue_t CfdpHandler::handlePduPacketMessages() {
|
|
ReturnValue_t status;
|
|
ReturnValue_t result = OK;
|
|
TmTcMessage pduMsg;
|
|
for (status = pduQueue.receiveMessage(&pduMsg); status == returnvalue::OK;
|
|
status = pduQueue.receiveMessage(&pduMsg)) {
|
|
result = handlePduPacket(pduMsg);
|
|
if (result != OK) {
|
|
// TODO: Maybe add printout with context specific information?
|
|
status = result;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
ReturnValue_t CfdpHandler::handleCfdpMessages() {
|
|
ReturnValue_t status;
|
|
ReturnValue_t result;
|
|
CommandMessage cfdpMsg;
|
|
for (status = cfdpRequestQueue.receiveMessage(&cfdpMsg); status == returnvalue::OK;
|
|
status = cfdpRequestQueue.receiveMessage(&cfdpMsg)) {
|
|
result = handleCfdpRequest(cfdpMsg);
|
|
if (result != OK) {
|
|
sif::warning << "Handling CFDP request failed with code 0x" << std::setw(4) << std::hex
|
|
<< result << std::dec << std::endl;
|
|
triggerEvent(cfdp::events::HANDLING_CFDP_REQUEST_FAILED, 0, result);
|
|
// TODO: Maybe add printout with context specific information?
|
|
status = result;
|
|
}
|
|
}
|
|
return status;
|
|
}
|