Compare commits
44 Commits
1b005d706a
...
mueller/ne
Author | SHA1 | Date | |
---|---|---|---|
39310c3fad | |||
d16c5024dc | |||
bf311757a2 | |||
a4531e4ced | |||
cbe1b65a33 | |||
80f0060cd2 | |||
97c629ad84 | |||
bf12f284fa | |||
8589f4d63a | |||
ca80589233 | |||
f2ebaed092 | |||
f0b89e98df | |||
5557d95994 | |||
fc24c9b5d8 | |||
7ef69c839c | |||
9b798d798e | |||
b13453f46b | |||
d0e322d7e2 | |||
ecde164f68 | |||
50930b41ba | |||
bf4ca56658 | |||
16ffa00155 | |||
f05295bada | |||
8199b8f359 | |||
9483c2809d | |||
009700ce80 | |||
1e43296f2b | |||
1aa062df7f | |||
14a8924a83 | |||
9f81926aec | |||
79c38b45df | |||
e893e73f86 | |||
bdbe0cc9da | |||
c38088c64b | |||
7eb63d6d79 | |||
ed68268c0c | |||
c549914efb | |||
753d5ff39e | |||
36ca35da77 | |||
4fb7375492 | |||
47df9e8b5b | |||
86aafe4422 | |||
dbaeed83af | |||
385a0ffd73 |
@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Fixes
|
||||
|
||||
- TC Scheduler Service 11: Add size and CRC check for contained TC.
|
||||
- Only delete health table entry in `HealthHelper` destructor if
|
||||
health table was set.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/710/files
|
||||
@ -27,6 +28,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Added
|
||||
|
||||
- `DleParser` helper class to parse DLE encoded packets from a byte stream.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/711
|
||||
- `UioMapper` is able to resolve symlinks now.
|
||||
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/709
|
||||
- Add new `UnsignedByteField` class
|
||||
|
@ -2,7 +2,10 @@
|
||||
#define FSFW_CFDP_H
|
||||
|
||||
#include "cfdp/definitions.h"
|
||||
#include "cfdp/handler/CfdpHandler.h"
|
||||
#include "cfdp/handler/DestHandler.h"
|
||||
#include "cfdp/handler/FaultHandlerBase.h"
|
||||
#include "cfdp/helpers.h"
|
||||
#include "cfdp/tlv/Lv.h"
|
||||
#include "cfdp/tlv/StringLv.h"
|
||||
#include "cfdp/tlv/Tlv.h"
|
||||
|
@ -1 +1,3 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE FaultHandlerBase.cpp UserBase.cpp)
|
||||
target_sources(
|
||||
${LIB_FSFW_NAME} PRIVATE SourceHandler.cpp DestHandler.cpp
|
||||
FaultHandlerBase.cpp UserBase.cpp CfdpHandler.cpp)
|
||||
|
134
src/fsfw/cfdp/handler/CfdpHandler.cpp
Normal file
134
src/fsfw/cfdp/handler/CfdpHandler.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
#include "CfdpHandler.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/tmtcservices/TmTcMessage.h"
|
||||
|
||||
using namespace returnvalue;
|
||||
using namespace cfdp;
|
||||
|
||||
CfdpHandler::CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg)
|
||||
: SystemObject(fsfwParams.objectId),
|
||||
msgQueue(fsfwParams.msgQueue),
|
||||
destHandler(
|
||||
DestHandlerParams(LocalEntityCfg(cfdpCfg.id, cfdpCfg.indicCfg, cfdpCfg.faultHandler),
|
||||
cfdpCfg.userHandler, cfdpCfg.remoteCfgProvider, cfdpCfg.packetInfoList,
|
||||
cfdpCfg.lostSegmentsList),
|
||||
FsfwParams(fsfwParams.packetDest, nullptr, this, fsfwParams.tcStore,
|
||||
fsfwParams.tmStore)) {
|
||||
destHandler.setMsgQueue(msgQueue);
|
||||
}
|
||||
|
||||
[[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 msgQueue.getId(); }
|
||||
|
||||
ReturnValue_t CfdpHandler::initialize() {
|
||||
ReturnValue_t result = destHandler.initialize();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
tcStore = destHandler.getTcStore();
|
||||
tmStore = destHandler.getTmStore();
|
||||
|
||||
return SystemObject::initialize();
|
||||
}
|
||||
|
||||
ReturnValue_t CfdpHandler::performOperation(uint8_t operationCode) {
|
||||
// TODO: Receive TC packets and route them to source and dest handler, depending on which is
|
||||
// correct or more appropriate
|
||||
ReturnValue_t status;
|
||||
ReturnValue_t result = OK;
|
||||
TmTcMessage tmtcMsg;
|
||||
for (status = msgQueue.receiveMessage(&tmtcMsg); status == returnvalue::OK;
|
||||
status = msgQueue.receiveMessage(&tmtcMsg)) {
|
||||
result = handleCfdpPacket(tmtcMsg);
|
||||
if (result != OK) {
|
||||
status = result;
|
||||
}
|
||||
}
|
||||
auto& fsmRes = destHandler.performStateMachine();
|
||||
// TODO: Error handling?
|
||||
while (fsmRes.callStatus == CallStatus::CALL_AGAIN) {
|
||||
destHandler.performStateMachine();
|
||||
// TODO: Error handling?
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
ReturnValue_t CfdpHandler::handleCfdpPacket(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])) {
|
||||
return INVALID_DIRECTIVE_FIELD;
|
||||
}
|
||||
auto directive = static_cast<FileDirective>(pduDataField[0]);
|
||||
|
||||
auto passToDestHandler = [&]() {
|
||||
accessorPair.second.release();
|
||||
PacketInfo info(type, msg.getStorageId(), directive);
|
||||
result = destHandler.passPacket(info);
|
||||
};
|
||||
auto passToSourceHandler = [&]() {
|
||||
|
||||
};
|
||||
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
|
||||
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) {
|
||||
passToDestHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
71
src/fsfw/cfdp/handler/CfdpHandler.h
Normal file
71
src/fsfw/cfdp/handler/CfdpHandler.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
|
||||
#define FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "fsfw/cfdp/handler/DestHandler.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||
#include "fsfw/tmtcservices/AcceptsTelecommandsIF.h"
|
||||
#include "fsfw/tmtcservices/TmTcMessage.h"
|
||||
|
||||
struct FsfwHandlerParams {
|
||||
FsfwHandlerParams(object_id_t objectId, HasFileSystemIF& vfs, AcceptsTelemetryIF& packetDest,
|
||||
StorageManagerIF& tcStore, StorageManagerIF& tmStore, MessageQueueIF& msgQueue)
|
||||
: objectId(objectId),
|
||||
vfs(vfs),
|
||||
packetDest(packetDest),
|
||||
tcStore(tcStore),
|
||||
tmStore(tmStore),
|
||||
msgQueue(msgQueue) {}
|
||||
object_id_t objectId{};
|
||||
HasFileSystemIF& vfs;
|
||||
AcceptsTelemetryIF& packetDest;
|
||||
StorageManagerIF& tcStore;
|
||||
StorageManagerIF& tmStore;
|
||||
MessageQueueIF& msgQueue;
|
||||
};
|
||||
|
||||
struct CfdpHandlerCfg {
|
||||
CfdpHandlerCfg(cfdp::EntityId localId, cfdp::IndicationCfg indicationCfg,
|
||||
cfdp::UserBase& userHandler, cfdp::FaultHandlerBase& userFaultHandler,
|
||||
cfdp::PacketInfoListBase& packetInfo, cfdp::LostSegmentsListBase& lostSegmentsList,
|
||||
cfdp::RemoteConfigTableIF& remoteCfgProvider)
|
||||
: id(std::move(localId)),
|
||||
indicCfg(indicationCfg),
|
||||
packetInfoList(packetInfo),
|
||||
lostSegmentsList(lostSegmentsList),
|
||||
remoteCfgProvider(remoteCfgProvider),
|
||||
userHandler(userHandler),
|
||||
faultHandler(userFaultHandler) {}
|
||||
|
||||
cfdp::EntityId id;
|
||||
cfdp::IndicationCfg indicCfg;
|
||||
cfdp::PacketInfoListBase& packetInfoList;
|
||||
cfdp::LostSegmentsListBase& lostSegmentsList;
|
||||
cfdp::RemoteConfigTableIF& remoteCfgProvider;
|
||||
cfdp::UserBase& userHandler;
|
||||
cfdp::FaultHandlerBase& faultHandler;
|
||||
};
|
||||
|
||||
class CfdpHandler : public SystemObject, public ExecutableObjectIF, public AcceptsTelecommandsIF {
|
||||
public:
|
||||
explicit CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg);
|
||||
|
||||
[[nodiscard]] const char* getName() const override;
|
||||
[[nodiscard]] uint32_t getIdentifier() const override;
|
||||
[[nodiscard]] MessageQueueId_t getRequestQueue() const override;
|
||||
|
||||
ReturnValue_t initialize() override;
|
||||
ReturnValue_t performOperation(uint8_t operationCode) override;
|
||||
|
||||
private:
|
||||
MessageQueueIF& msgQueue;
|
||||
cfdp::DestHandler destHandler;
|
||||
StorageManagerIF* tcStore = nullptr;
|
||||
StorageManagerIF* tmStore = nullptr;
|
||||
|
||||
ReturnValue_t handleCfdpPacket(TmTcMessage& msg);
|
||||
};
|
||||
|
||||
#endif // FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
|
490
src/fsfw/cfdp/handler/DestHandler.cpp
Normal file
490
src/fsfw/cfdp/handler/DestHandler.cpp
Normal file
@ -0,0 +1,490 @@
|
||||
#include "DestHandler.h"
|
||||
|
||||
#include <etl/crc32.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "fsfw/FSFW.h"
|
||||
#include "fsfw/cfdp/pdu/EofPduReader.h"
|
||||
#include "fsfw/cfdp/pdu/FileDataReader.h"
|
||||
#include "fsfw/cfdp/pdu/FinishedPduCreator.h"
|
||||
#include "fsfw/cfdp/pdu/PduHeaderReader.h"
|
||||
#include "fsfw/objectmanager.h"
|
||||
#include "fsfw/tmtcservices/TmTcMessage.h"
|
||||
|
||||
using namespace returnvalue;
|
||||
|
||||
cfdp::DestHandler::DestHandler(DestHandlerParams params, FsfwParams fsfwParams)
|
||||
: tlvVec(params.maxTlvsInOnePdu),
|
||||
userTlvVec(params.maxTlvsInOnePdu),
|
||||
dp(std::move(params)),
|
||||
fp(fsfwParams),
|
||||
tp(params.maxFilenameLen) {
|
||||
tp.pduConf.direction = cfdp::Direction::TOWARDS_SENDER;
|
||||
}
|
||||
|
||||
const cfdp::DestHandler::FsmResult& cfdp::DestHandler::performStateMachine() {
|
||||
ReturnValue_t result;
|
||||
uint8_t errorIdx = 0;
|
||||
fsmRes.resetOfIteration();
|
||||
if (fsmRes.step == TransactionStep::IDLE) {
|
||||
for (auto infoIter = dp.packetListRef.begin(); infoIter != dp.packetListRef.end();) {
|
||||
if (infoIter->pduType == PduType::FILE_DIRECTIVE and
|
||||
infoIter->directiveType == FileDirective::METADATA) {
|
||||
result = handleMetadataPdu(*infoIter);
|
||||
checkAndHandleError(result, errorIdx);
|
||||
// Store data was deleted in PDU handler because a store guard is used
|
||||
dp.packetListRef.erase(infoIter++);
|
||||
} else {
|
||||
infoIter++;
|
||||
}
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::IDLE) {
|
||||
// To decrease the already high complexity of the software, all packets arriving before
|
||||
// a metadata PDU are deleted.
|
||||
for (auto infoIter = dp.packetListRef.begin(); infoIter != dp.packetListRef.end();) {
|
||||
fp.tcStore->deleteData(infoIter->storeId);
|
||||
infoIter++;
|
||||
}
|
||||
dp.packetListRef.clear();
|
||||
}
|
||||
|
||||
if (fsmRes.step != TransactionStep::IDLE) {
|
||||
fsmRes.callStatus = CallStatus::CALL_AGAIN;
|
||||
}
|
||||
return updateFsmRes(errorIdx);
|
||||
}
|
||||
if (fsmRes.state == CfdpStates::BUSY_CLASS_1_NACKED) {
|
||||
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
|
||||
for (auto infoIter = dp.packetListRef.begin(); infoIter != dp.packetListRef.end();) {
|
||||
if (infoIter->pduType == PduType::FILE_DATA) {
|
||||
result = handleFileDataPdu(*infoIter);
|
||||
checkAndHandleError(result, errorIdx);
|
||||
// Store data was deleted in PDU handler because a store guard is used
|
||||
dp.packetListRef.erase(infoIter++);
|
||||
} else if (infoIter->pduType == PduType::FILE_DIRECTIVE and
|
||||
infoIter->directiveType == FileDirective::EOF_DIRECTIVE) {
|
||||
// TODO: Support for check timer missing
|
||||
result = handleEofPdu(*infoIter);
|
||||
checkAndHandleError(result, errorIdx);
|
||||
// Store data was deleted in PDU handler because a store guard is used
|
||||
dp.packetListRef.erase(infoIter++);
|
||||
} else {
|
||||
infoIter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::TRANSFER_COMPLETION) {
|
||||
result = handleTransferCompletion();
|
||||
checkAndHandleError(result, errorIdx);
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::SENDING_FINISHED_PDU) {
|
||||
result = sendFinishedPdu();
|
||||
checkAndHandleError(result, errorIdx);
|
||||
finish();
|
||||
}
|
||||
return updateFsmRes(errorIdx);
|
||||
}
|
||||
if (fsmRes.state == CfdpStates::BUSY_CLASS_2_ACKED) {
|
||||
// TODO: Will be implemented at a later stage
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "CFDP state machine for acknowledged mode not implemented yet" << std::endl;
|
||||
#endif
|
||||
}
|
||||
return updateFsmRes(errorIdx);
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::passPacket(PacketInfo packet) {
|
||||
if (dp.packetListRef.full()) {
|
||||
return FAILED;
|
||||
}
|
||||
dp.packetListRef.push_back(packet);
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::initialize() {
|
||||
if (fp.tmStore == nullptr) {
|
||||
fp.tmStore = ObjectManager::instance()->get<StorageManagerIF>(objects::TM_STORE);
|
||||
if (fp.tmStore == nullptr) {
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if (fp.tcStore == nullptr) {
|
||||
fp.tcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::TC_STORE);
|
||||
if (fp.tcStore == nullptr) {
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if (fp.msgQueue == nullptr) {
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleMetadataPdu(const PacketInfo& info) {
|
||||
// Process metadata PDU
|
||||
auto constAccessorPair = fp.tcStore->getData(info.storeId);
|
||||
if (constAccessorPair.first != OK) {
|
||||
// TODO: This is not a CFDP error. Event and/or warning?
|
||||
return constAccessorPair.first;
|
||||
}
|
||||
cfdp::StringLv sourceFileName;
|
||||
cfdp::StringLv destFileName;
|
||||
MetadataInfo metadataInfo(tp.fileSize, sourceFileName, destFileName);
|
||||
cfdp::Tlv* tlvArrayAsPtr = tlvVec.data();
|
||||
metadataInfo.setOptionsArray(&tlvArrayAsPtr, std::nullopt, tlvVec.size());
|
||||
MetadataPduReader reader(constAccessorPair.second.data(), constAccessorPair.second.size(),
|
||||
metadataInfo);
|
||||
ReturnValue_t result = reader.parseData();
|
||||
// TODO: The standard does not really specify what happens if this kind of error happens
|
||||
// I think it might be a good idea to cache some sort of error code, which
|
||||
// is translated into a warning and/or event by an upper layer
|
||||
if (result != OK) {
|
||||
return handleMetadataParseError(result, constAccessorPair.second.data(),
|
||||
constAccessorPair.second.size());
|
||||
}
|
||||
return startTransaction(reader, metadataInfo);
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleFileDataPdu(const cfdp::PacketInfo& info) {
|
||||
// Process file data PDU
|
||||
auto constAccessorPair = fp.tcStore->getData(info.storeId);
|
||||
if (constAccessorPair.first != OK) {
|
||||
// TODO: This is not a CFDP error. Event and/or warning?
|
||||
return constAccessorPair.first;
|
||||
}
|
||||
cfdp::FileSize offset;
|
||||
FileDataInfo fdInfo(offset);
|
||||
FileDataReader reader(constAccessorPair.second.data(), constAccessorPair.second.size(), fdInfo);
|
||||
ReturnValue_t result = reader.parseData();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
size_t fileSegmentLen = 0;
|
||||
const uint8_t* fileData = fdInfo.getFileData(&fileSegmentLen);
|
||||
FileOpParams fileOpParams(tp.destName.data(), fileSegmentLen);
|
||||
fileOpParams.offset = offset.value();
|
||||
if (dp.cfg.indicCfg.fileSegmentRecvIndicRequired) {
|
||||
FileSegmentRecvdParams segParams;
|
||||
segParams.offset = offset.value();
|
||||
segParams.id = tp.transactionId;
|
||||
segParams.length = fileSegmentLen;
|
||||
segParams.recContState = fdInfo.getRecordContinuationState();
|
||||
size_t segmentMetadatLen = 0;
|
||||
auto* segMetadata = fdInfo.getSegmentMetadata(&segmentMetadatLen);
|
||||
segParams.segmentMetadata = {segMetadata, segmentMetadatLen};
|
||||
dp.user.fileSegmentRecvdIndication(segParams);
|
||||
}
|
||||
result = dp.user.vfs.writeToFile(fileOpParams, fileData);
|
||||
if (offset.value() + fileSegmentLen > tp.progress) {
|
||||
tp.progress = offset.value() + fileSegmentLen;
|
||||
}
|
||||
if (result != returnvalue::OK) {
|
||||
// TODO: Proper Error handling
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "File write error" << std::endl;
|
||||
#endif
|
||||
} else {
|
||||
tp.deliveryStatus = FileDeliveryStatus::RETAINED_IN_FILESTORE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleEofPdu(const cfdp::PacketInfo& info) {
|
||||
// Process EOF PDU
|
||||
auto constAccessorPair = fp.tcStore->getData(info.storeId);
|
||||
if (constAccessorPair.first != OK) {
|
||||
// TODO: This is not a CFDP error. Event and/or warning?
|
||||
return constAccessorPair.first;
|
||||
}
|
||||
EofInfo eofInfo(nullptr);
|
||||
EofPduReader reader(constAccessorPair.second.data(), constAccessorPair.second.size(), eofInfo);
|
||||
ReturnValue_t result = reader.parseData();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
// TODO: Error handling
|
||||
if (eofInfo.getConditionCode() == ConditionCode::NO_ERROR) {
|
||||
tp.crc = eofInfo.getChecksum();
|
||||
uint64_t fileSizeFromEof = eofInfo.getFileSize().value();
|
||||
// CFDP 4.6.1.2.9: Declare file size error if progress exceeds file size
|
||||
if (fileSizeFromEof > tp.progress) {
|
||||
// TODO: File size error
|
||||
}
|
||||
tp.fileSize.setFileSize(fileSizeFromEof, std::nullopt);
|
||||
}
|
||||
if (dp.cfg.indicCfg.eofRecvIndicRequired) {
|
||||
dp.user.eofRecvIndication(getTransactionId());
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
|
||||
if (fsmRes.state == CfdpStates::BUSY_CLASS_1_NACKED) {
|
||||
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
|
||||
} else if (fsmRes.state == CfdpStates::BUSY_CLASS_2_ACKED) {
|
||||
fsmRes.step = TransactionStep::SENDING_ACK_PDU;
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleMetadataParseError(ReturnValue_t result,
|
||||
const uint8_t* rawData, size_t maxSize) {
|
||||
// TODO: try to extract destination ID for error
|
||||
// TODO: Invalid metadata PDU.
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "Parsing Metadata PDU failed with code " << result << std::endl;
|
||||
#else
|
||||
#endif
|
||||
PduHeaderReader headerReader(rawData, maxSize);
|
||||
result = headerReader.parseData();
|
||||
if (result != OK) {
|
||||
// TODO: Now this really should not happen. Warning or error,
|
||||
// yield or cache appropriate returnvalue
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "Parsing Header failed" << std::endl;
|
||||
#else
|
||||
#endif
|
||||
// TODO: Trigger appropriate event
|
||||
return result;
|
||||
}
|
||||
cfdp::EntityId destId;
|
||||
headerReader.getDestId(destId);
|
||||
RemoteEntityCfg* remoteCfg;
|
||||
if (not dp.remoteCfgTable.getRemoteCfg(destId, &remoteCfg)) {
|
||||
// TODO: No remote config for dest ID. I consider this a configuration error, which is not
|
||||
// covered by the standard.
|
||||
// Warning or error, yield or cache appropriate returnvalue
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "No remote config exists for destination ID" << std::endl;
|
||||
#else
|
||||
#endif
|
||||
// TODO: Trigger appropriate event
|
||||
}
|
||||
// TODO: Appropriate returnvalue?
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::startTransaction(MetadataPduReader& reader, MetadataInfo& info) {
|
||||
if (fsmRes.state != CfdpStates::IDLE) {
|
||||
// According to standard, discard metadata PDU if we are busy
|
||||
return OK;
|
||||
}
|
||||
ReturnValue_t result = OK;
|
||||
fsmRes.step = TransactionStep::TRANSACTION_START;
|
||||
if (reader.getTransmissionMode() == TransmissionMode::UNACKNOWLEDGED) {
|
||||
fsmRes.state = CfdpStates::BUSY_CLASS_1_NACKED;
|
||||
} else if (reader.getTransmissionMode() == TransmissionMode::ACKNOWLEDGED) {
|
||||
fsmRes.state = CfdpStates::BUSY_CLASS_2_ACKED;
|
||||
}
|
||||
tp.checksumType = info.getChecksumType();
|
||||
tp.closureRequested = info.isClosureRequested();
|
||||
size_t sourceNameSize = 0;
|
||||
const uint8_t* sourceNamePtr = info.getSourceFileName().getValue(&sourceNameSize);
|
||||
if (sourceNameSize > tp.sourceName.size()) {
|
||||
// TODO: Warning, event etc.
|
||||
return FAILED;
|
||||
}
|
||||
std::memcpy(tp.sourceName.data(), sourceNamePtr, sourceNameSize);
|
||||
tp.sourceName[sourceNameSize] = '\0';
|
||||
size_t destNameSize = 0;
|
||||
const uint8_t* destNamePtr = info.getDestFileName().getValue(&destNameSize);
|
||||
if (destNameSize > tp.destName.size()) {
|
||||
// TODO: Warning, event etc.
|
||||
return FAILED;
|
||||
}
|
||||
std::memcpy(tp.destName.data(), destNamePtr, destNameSize);
|
||||
tp.destName[destNameSize] = '\0';
|
||||
reader.fillConfig(tp.pduConf);
|
||||
tp.pduConf.direction = Direction::TOWARDS_SENDER;
|
||||
tp.transactionId.entityId = tp.pduConf.sourceId;
|
||||
tp.transactionId.seqNum = tp.pduConf.seqNum;
|
||||
if (not dp.remoteCfgTable.getRemoteCfg(tp.pduConf.sourceId, &tp.remoteCfg)) {
|
||||
// TODO: Warning, event etc.
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "cfdp::DestHandler" << __func__
|
||||
<< ": No remote configuration found for destination ID "
|
||||
<< tp.pduConf.sourceId.getValue() << std::endl;
|
||||
#endif
|
||||
return FAILED;
|
||||
}
|
||||
// If both dest name size and source name size are 0, we are dealing with a metadata only PDU,
|
||||
// so there is no need to create a file or truncate an existing file
|
||||
if (destNameSize > 0 and sourceNameSize > 0) {
|
||||
FilesystemParams fparams(tp.destName.data());
|
||||
// TODO: Filesystem errors?
|
||||
if (dp.user.vfs.fileExists(fparams)) {
|
||||
dp.user.vfs.truncateFile(fparams);
|
||||
} else {
|
||||
result = dp.user.vfs.createFile(fparams);
|
||||
if (result != OK) {
|
||||
// TODO: Handle FS error. This is probably a case for the filestore rejection mechanism of
|
||||
// CFDP.
|
||||
// In any case, it does not really make sense to continue here
|
||||
}
|
||||
}
|
||||
}
|
||||
fsmRes.step = TransactionStep::RECEIVING_FILE_DATA_PDUS;
|
||||
MetadataRecvdParams params(tp.transactionId, tp.pduConf.sourceId);
|
||||
params.fileSize = tp.fileSize.getSize();
|
||||
params.destFileName = tp.destName.data();
|
||||
params.sourceFileName = tp.sourceName.data();
|
||||
params.msgsToUserArray = dynamic_cast<MessageToUserTlv*>(userTlvVec.data());
|
||||
params.msgsToUserLen = info.getOptionsLen();
|
||||
dp.user.metadataRecvdIndication(params);
|
||||
return result;
|
||||
}
|
||||
|
||||
cfdp::CfdpStates cfdp::DestHandler::getCfdpState() const { return fsmRes.state; }
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleTransferCompletion() {
|
||||
ReturnValue_t result;
|
||||
if (tp.checksumType != ChecksumType::NULL_CHECKSUM) {
|
||||
result = checksumVerification();
|
||||
if (result != OK) {
|
||||
// TODO: Warning / error handling?
|
||||
}
|
||||
} else {
|
||||
tp.conditionCode = ConditionCode::NO_ERROR;
|
||||
}
|
||||
result = noticeOfCompletion();
|
||||
if (result != OK) {
|
||||
}
|
||||
if (fsmRes.state == CfdpStates::BUSY_CLASS_1_NACKED) {
|
||||
if (tp.closureRequested) {
|
||||
fsmRes.step = TransactionStep::SENDING_FINISHED_PDU;
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
} else if (fsmRes.state == CfdpStates::BUSY_CLASS_2_ACKED) {
|
||||
fsmRes.step = TransactionStep::SENDING_FINISHED_PDU;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void cfdp::DestHandler::finish() {
|
||||
tp.reset();
|
||||
dp.packetListRef.clear();
|
||||
fsmRes.state = CfdpStates::IDLE;
|
||||
fsmRes.step = TransactionStep::IDLE;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::checksumVerification() {
|
||||
std::array<uint8_t, 1024> buf{};
|
||||
// TODO: Checksum verification and notice of completion
|
||||
etl::crc32 crcCalc;
|
||||
uint64_t currentOffset = 0;
|
||||
FileOpParams params(tp.destName.data(), tp.fileSize.value());
|
||||
while (currentOffset < tp.fileSize.value()) {
|
||||
uint64_t readLen;
|
||||
if (currentOffset + buf.size() > tp.fileSize.value()) {
|
||||
readLen = tp.fileSize.value() - currentOffset;
|
||||
} else {
|
||||
readLen = buf.size();
|
||||
}
|
||||
if (readLen > 0) {
|
||||
params.offset = currentOffset;
|
||||
params.size = readLen;
|
||||
auto result = dp.user.vfs.readFromFile(params, buf.data(), buf.size());
|
||||
if (result != OK) {
|
||||
// TODO: I think this is a case for a filestore rejection, but it might sense to print
|
||||
// a warning or trigger an event because this should generally not happen
|
||||
return FAILED;
|
||||
}
|
||||
crcCalc.add(buf.begin(), buf.begin() + readLen);
|
||||
}
|
||||
currentOffset += readLen;
|
||||
}
|
||||
|
||||
uint32_t value = crcCalc.value();
|
||||
if (value == tp.crc) {
|
||||
tp.conditionCode = ConditionCode::NO_ERROR;
|
||||
tp.deliveryCode = FileDeliveryCode::DATA_COMPLETE;
|
||||
} else {
|
||||
// TODO: Proper error handling
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "CRC check for file " << tp.destName.data() << " failed" << std::endl;
|
||||
#endif
|
||||
tp.conditionCode = ConditionCode::FILE_CHECKSUM_FAILURE;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::noticeOfCompletion() {
|
||||
if (dp.cfg.indicCfg.transactionFinishedIndicRequired) {
|
||||
TransactionFinishedParams params(tp.transactionId, tp.conditionCode, tp.deliveryCode,
|
||||
tp.deliveryStatus);
|
||||
dp.user.transactionFinishedIndication(params);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::sendFinishedPdu() {
|
||||
FinishedInfo info(tp.conditionCode, tp.deliveryCode, tp.deliveryStatus);
|
||||
FinishPduCreator finishedPdu(tp.pduConf, info);
|
||||
store_address_t storeId;
|
||||
uint8_t* dataPtr = nullptr;
|
||||
ReturnValue_t result =
|
||||
fp.tmStore->getFreeElement(&storeId, finishedPdu.getSerializedSize(), &dataPtr);
|
||||
if (result != OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "cfdp::DestHandler:sendFinishedPdu: Getting store slot failed" << std::endl;
|
||||
#endif
|
||||
fp.eventReporter->forwardEvent(events::STORE_ERROR, result, 0);
|
||||
return result;
|
||||
}
|
||||
size_t serLen = 0;
|
||||
result = finishedPdu.serialize(dataPtr, serLen, finishedPdu.getSerializedSize());
|
||||
if (result != OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "cfdp::DestHandler::sendFinishedPdu: Serializing Finished PDU failed"
|
||||
<< std::endl;
|
||||
#endif
|
||||
fp.eventReporter->forwardEvent(events::SERIALIZATION_ERROR, result, 0);
|
||||
return result;
|
||||
}
|
||||
TmTcMessage msg(storeId);
|
||||
result = fp.msgQueue->sendMessage(fp.packetDest.getReportReceptionQueue(), &msg);
|
||||
if (result != OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "cfdp::DestHandler::sendFinishedPdu: Sending PDU failed" << std::endl;
|
||||
#endif
|
||||
fp.eventReporter->forwardEvent(events::MSG_QUEUE_ERROR, result, 0);
|
||||
return result;
|
||||
}
|
||||
fsmRes.packetsSent++;
|
||||
return OK;
|
||||
}
|
||||
|
||||
cfdp::DestHandler::TransactionStep cfdp::DestHandler::getTransactionStep() const {
|
||||
return fsmRes.step;
|
||||
}
|
||||
|
||||
const cfdp::DestHandler::FsmResult& cfdp::DestHandler::updateFsmRes(uint8_t errors) {
|
||||
fsmRes.errors = errors;
|
||||
fsmRes.result = OK;
|
||||
if (fsmRes.errors > 0) {
|
||||
fsmRes.result = FAILED;
|
||||
}
|
||||
return fsmRes;
|
||||
}
|
||||
|
||||
const cfdp::TransactionId& cfdp::DestHandler::getTransactionId() const { return tp.transactionId; }
|
||||
|
||||
void cfdp::DestHandler::checkAndHandleError(ReturnValue_t result, uint8_t& errorIdx) {
|
||||
if (result != OK and errorIdx < 3) {
|
||||
fsmRes.errorCodes[errorIdx] = result;
|
||||
errorIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
void cfdp::DestHandler::setMsgQueue(MessageQueueIF& queue) { fp.msgQueue = &queue; }
|
||||
|
||||
void cfdp::DestHandler::setEventReporter(EventReportingProxyIF& reporter) {
|
||||
fp.eventReporter = &reporter;
|
||||
}
|
||||
|
||||
const cfdp::DestHandlerParams& cfdp::DestHandler::getDestHandlerParams() const { return dp; }
|
||||
|
||||
StorageManagerIF* cfdp::DestHandler::getTmStore() const { return fp.tmStore; }
|
||||
StorageManagerIF* cfdp::DestHandler::getTcStore() const { return fp.tcStore; }
|
202
src/fsfw/cfdp/handler/DestHandler.h
Normal file
202
src/fsfw/cfdp/handler/DestHandler.h
Normal file
@ -0,0 +1,202 @@
|
||||
#ifndef FSFW_CFDP_CFDPDESTHANDLER_H
|
||||
#define FSFW_CFDP_CFDPDESTHANDLER_H
|
||||
|
||||
#include <etl/list.h>
|
||||
#include <etl/set.h>
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include "RemoteConfigTableIF.h"
|
||||
#include "UserBase.h"
|
||||
#include "defs.h"
|
||||
#include "fsfw/cfdp/handler/mib.h"
|
||||
#include "fsfw/cfdp/pdu/MetadataPduReader.h"
|
||||
#include "fsfw/cfdp/pdu/PduConfig.h"
|
||||
#include "fsfw/container/DynamicFIFO.h"
|
||||
#include "fsfw/storagemanager/StorageManagerIF.h"
|
||||
#include "fsfw/storagemanager/storeAddress.h"
|
||||
#include "fsfw/tmtcservices/AcceptsTelemetryIF.h"
|
||||
|
||||
namespace cfdp {
|
||||
|
||||
struct PacketInfo {
|
||||
PacketInfo(PduType type, store_address_t storeId,
|
||||
std::optional<FileDirective> directive = std::nullopt)
|
||||
: pduType(type), directiveType(directive), storeId(storeId) {}
|
||||
|
||||
PduType pduType = PduType::FILE_DATA;
|
||||
std::optional<FileDirective> directiveType = FileDirective::INVALID_DIRECTIVE;
|
||||
store_address_t storeId = store_address_t::invalid();
|
||||
PacketInfo() = default;
|
||||
};
|
||||
|
||||
template <size_t SIZE>
|
||||
using LostSegmentsList = etl::set<etl::pair<uint64_t, uint64_t>, SIZE>;
|
||||
template <size_t SIZE>
|
||||
using PacketInfoList = etl::list<PacketInfo, SIZE>;
|
||||
using LostSegmentsListBase = etl::iset<etl::pair<uint64_t, uint64_t>>;
|
||||
using PacketInfoListBase = etl::ilist<PacketInfo>;
|
||||
|
||||
struct DestHandlerParams {
|
||||
DestHandlerParams(LocalEntityCfg cfg, UserBase& user, RemoteConfigTableIF& remoteCfgTable,
|
||||
PacketInfoListBase& packetList,
|
||||
// TODO: This container can potentially take tons of space. For a better
|
||||
// memory efficient implementation, an additional abstraction could be
|
||||
// be used so users can use uint32_t as the pair type
|
||||
LostSegmentsListBase& lostSegmentsContainer)
|
||||
: cfg(std::move(cfg)),
|
||||
user(user),
|
||||
remoteCfgTable(remoteCfgTable),
|
||||
packetListRef(packetList),
|
||||
lostSegmentsContainer(lostSegmentsContainer) {}
|
||||
|
||||
LocalEntityCfg cfg;
|
||||
UserBase& user;
|
||||
RemoteConfigTableIF& remoteCfgTable;
|
||||
|
||||
PacketInfoListBase& packetListRef;
|
||||
LostSegmentsListBase& lostSegmentsContainer;
|
||||
uint8_t maxTlvsInOnePdu = 10;
|
||||
size_t maxFilenameLen = 255;
|
||||
};
|
||||
|
||||
struct FsfwParams {
|
||||
FsfwParams(AcceptsTelemetryIF& packetDest, MessageQueueIF* msgQueue,
|
||||
EventReportingProxyIF* eventReporter, StorageManagerIF& tcStore,
|
||||
StorageManagerIF& tmStore)
|
||||
: FsfwParams(packetDest, msgQueue, eventReporter) {
|
||||
this->tcStore = &tcStore;
|
||||
this->tmStore = &tmStore;
|
||||
}
|
||||
|
||||
FsfwParams(AcceptsTelemetryIF& packetDest, MessageQueueIF* msgQueue,
|
||||
EventReportingProxyIF* eventReporter)
|
||||
: packetDest(packetDest), msgQueue(msgQueue), eventReporter(eventReporter) {}
|
||||
AcceptsTelemetryIF& packetDest;
|
||||
MessageQueueIF* msgQueue;
|
||||
EventReportingProxyIF* eventReporter = nullptr;
|
||||
StorageManagerIF* tcStore = nullptr;
|
||||
StorageManagerIF* tmStore = nullptr;
|
||||
};
|
||||
|
||||
enum class CallStatus { DONE, CALL_AFTER_DELAY, CALL_AGAIN };
|
||||
|
||||
class DestHandler {
|
||||
public:
|
||||
enum class TransactionStep {
|
||||
IDLE = 0,
|
||||
TRANSACTION_START = 1,
|
||||
RECEIVING_FILE_DATA_PDUS = 2,
|
||||
SENDING_ACK_PDU = 3,
|
||||
TRANSFER_COMPLETION = 4,
|
||||
SENDING_FINISHED_PDU = 5
|
||||
};
|
||||
|
||||
struct FsmResult {
|
||||
public:
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
CallStatus callStatus = CallStatus::CALL_AFTER_DELAY;
|
||||
TransactionStep step = TransactionStep::IDLE;
|
||||
CfdpStates state = CfdpStates::IDLE;
|
||||
uint32_t packetsSent = 0;
|
||||
uint8_t errors = 0;
|
||||
std::array<ReturnValue_t, 3> errorCodes = {};
|
||||
void resetOfIteration() {
|
||||
result = returnvalue::OK;
|
||||
callStatus = CallStatus::CALL_AFTER_DELAY;
|
||||
packetsSent = 0;
|
||||
errors = 0;
|
||||
errorCodes.fill(returnvalue::OK);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Will be returned if it is advisable to call the state machine operation call again
|
||||
*/
|
||||
ReturnValue_t PARTIAL_SUCCESS = returnvalue::makeCode(0, 2);
|
||||
ReturnValue_t FAILURE = returnvalue::makeCode(0, 3);
|
||||
explicit DestHandler(DestHandlerParams handlerParams, FsfwParams fsfwParams);
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* - @c returnvalue::OK State machine OK for this execution cycle
|
||||
* - @c CALL_FSM_AGAIN State machine should be called again.
|
||||
*/
|
||||
const FsmResult& performStateMachine();
|
||||
void setMsgQueue(MessageQueueIF& queue);
|
||||
void setEventReporter(EventReportingProxyIF& reporter);
|
||||
|
||||
ReturnValue_t passPacket(PacketInfo packet);
|
||||
|
||||
ReturnValue_t initialize();
|
||||
|
||||
[[nodiscard]] CfdpStates getCfdpState() const;
|
||||
[[nodiscard]] TransactionStep getTransactionStep() const;
|
||||
[[nodiscard]] const TransactionId& getTransactionId() const;
|
||||
[[nodiscard]] const DestHandlerParams& getDestHandlerParams() const;
|
||||
[[nodiscard]] StorageManagerIF* getTcStore() const;
|
||||
[[nodiscard]] StorageManagerIF* getTmStore() const;
|
||||
|
||||
private:
|
||||
struct TransactionParams {
|
||||
// Initialize char vectors with length + 1 for 0 termination
|
||||
explicit TransactionParams(size_t maxFileNameLen)
|
||||
: sourceName(maxFileNameLen + 1), destName(maxFileNameLen + 1) {}
|
||||
|
||||
void reset() {
|
||||
pduConf = PduConfig();
|
||||
transactionId = TransactionId();
|
||||
std::fill(sourceName.begin(), sourceName.end(), '\0');
|
||||
std::fill(destName.begin(), destName.end(), '\0');
|
||||
fileSize.setFileSize(0, false);
|
||||
conditionCode = ConditionCode::NO_ERROR;
|
||||
deliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
|
||||
deliveryStatus = FileDeliveryStatus::DISCARDED_DELIBERATELY;
|
||||
crc = 0;
|
||||
progress = 0;
|
||||
remoteCfg = nullptr;
|
||||
closureRequested = false;
|
||||
checksumType = ChecksumType::NULL_CHECKSUM;
|
||||
}
|
||||
|
||||
ChecksumType checksumType = ChecksumType::NULL_CHECKSUM;
|
||||
bool closureRequested = false;
|
||||
std::vector<char> sourceName;
|
||||
std::vector<char> destName;
|
||||
cfdp::FileSize fileSize;
|
||||
TransactionId transactionId;
|
||||
PduConfig pduConf;
|
||||
ConditionCode conditionCode = ConditionCode::NO_ERROR;
|
||||
FileDeliveryCode deliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
|
||||
FileDeliveryStatus deliveryStatus = FileDeliveryStatus::DISCARDED_DELIBERATELY;
|
||||
uint32_t crc = 0;
|
||||
uint64_t progress = 0;
|
||||
RemoteEntityCfg* remoteCfg = nullptr;
|
||||
};
|
||||
|
||||
std::vector<cfdp::Tlv> tlvVec;
|
||||
std::vector<cfdp::Tlv> userTlvVec;
|
||||
DestHandlerParams dp;
|
||||
FsfwParams fp;
|
||||
TransactionParams tp;
|
||||
FsmResult fsmRes;
|
||||
|
||||
ReturnValue_t startTransaction(MetadataPduReader& reader, MetadataInfo& info);
|
||||
ReturnValue_t handleMetadataPdu(const PacketInfo& info);
|
||||
ReturnValue_t handleFileDataPdu(const PacketInfo& info);
|
||||
ReturnValue_t handleEofPdu(const PacketInfo& info);
|
||||
ReturnValue_t handleMetadataParseError(ReturnValue_t result, const uint8_t* rawData,
|
||||
size_t maxSize);
|
||||
ReturnValue_t handleTransferCompletion();
|
||||
ReturnValue_t sendFinishedPdu();
|
||||
ReturnValue_t noticeOfCompletion();
|
||||
ReturnValue_t checksumVerification();
|
||||
const FsmResult& updateFsmRes(uint8_t errors);
|
||||
void checkAndHandleError(ReturnValue_t result, uint8_t& errorIdx);
|
||||
void finish();
|
||||
};
|
||||
|
||||
} // namespace cfdp
|
||||
|
||||
#endif // FSFW_CFDP_CFDPDESTHANDLER_H
|
1
src/fsfw/cfdp/handler/SourceHandler.cpp
Normal file
1
src/fsfw/cfdp/handler/SourceHandler.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "SourceHandler.h"
|
6
src/fsfw/cfdp/handler/SourceHandler.h
Normal file
6
src/fsfw/cfdp/handler/SourceHandler.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef FSFW_CFDP_CFDPSOURCEHANDLER_H
|
||||
#define FSFW_CFDP_CFDPSOURCEHANDLER_H
|
||||
|
||||
class SourceHandler {};
|
||||
|
||||
#endif // FSFW_CFDP_CFDPSOURCEHANDLER_H
|
@ -5,5 +5,15 @@ namespace cfdp {
|
||||
|
||||
enum class CfdpStates { IDLE, BUSY_CLASS_1_NACKED, BUSY_CLASS_2_ACKED, SUSPENDED };
|
||||
|
||||
}
|
||||
static constexpr uint8_t SSID = SUBSYSTEM_ID::CFDP;
|
||||
|
||||
namespace events {
|
||||
|
||||
static constexpr Event STORE_ERROR = event::makeEvent(SSID, 0, severity::LOW);
|
||||
static constexpr Event MSG_QUEUE_ERROR = event::makeEvent(SSID, 1, severity::LOW);
|
||||
static constexpr Event SERIALIZATION_ERROR = event::makeEvent(SSID, 2, severity::LOW);
|
||||
|
||||
} // namespace events
|
||||
|
||||
} // namespace cfdp
|
||||
#endif // FSFW_CFDP_HANDLER_DEFS_H
|
||||
|
@ -359,6 +359,8 @@ void DeviceHandlerBase::doStateMachine() {
|
||||
if ((switchState == PowerSwitchIF::SWITCH_ON) || (switchState == NO_SWITCH)) {
|
||||
// NOTE: TransitionSourceMode and -SubMode are set by handleCommandedModeTransition
|
||||
childTransitionFailure = CHILD_TIMEOUT;
|
||||
transitionSourceMode = _MODE_SHUT_DOWN;
|
||||
transitionSourceSubMode = SUBMODE_NONE;
|
||||
setMode(_MODE_START_UP);
|
||||
callChildStatemachine();
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ enum : uint8_t {
|
||||
PUS_SERVICE_23 = 103,
|
||||
MGM_LIS3MDL = 106,
|
||||
MGM_RM3100 = 107,
|
||||
CFDP = 108,
|
||||
|
||||
FW_SUBSYSTEM_ID_RANGE
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ target_sources(
|
||||
AsciiConverter.cpp
|
||||
CRC.cpp
|
||||
DleEncoder.cpp
|
||||
DleParser.cpp
|
||||
PeriodicOperationDivider.cpp
|
||||
timevalOperations.cpp
|
||||
Type.cpp
|
||||
|
173
src/fsfw/globalfunctions/DleParser.cpp
Normal file
173
src/fsfw/globalfunctions/DleParser.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
#include "DleParser.h"
|
||||
|
||||
#include <fsfw/serviceinterface/ServiceInterface.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
DleParser::DleParser(SimpleRingBuffer& decodeRingBuf, DleEncoder& decoder, BufPair encodedBuf,
|
||||
BufPair decodedBuf)
|
||||
: decodeRingBuf(decodeRingBuf),
|
||||
decoder(decoder),
|
||||
encodedBuf(encodedBuf),
|
||||
decodedBuf(decodedBuf) {}
|
||||
|
||||
ReturnValue_t DleParser::passData(const uint8_t* data, size_t len) {
|
||||
if (data == nullptr or len == 0) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
return decodeRingBuf.writeData(data, len);
|
||||
}
|
||||
|
||||
ReturnValue_t DleParser::parseRingBuf(size_t& readSize) {
|
||||
ctx.setType(DleParser::ContextType::NONE);
|
||||
size_t availableData = decodeRingBuf.getAvailableReadData();
|
||||
if (availableData == 0) {
|
||||
return NO_PACKET_FOUND;
|
||||
}
|
||||
if (availableData > encodedBuf.second) {
|
||||
ErrorInfo info;
|
||||
info.len = decodeRingBuf.getAvailableReadData();
|
||||
setErrorContext(ErrorTypes::DECODING_BUF_TOO_SMALL, info);
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
ReturnValue_t result = decodeRingBuf.readData(encodedBuf.first, availableData);
|
||||
if (result != returnvalue::OK) {
|
||||
ErrorInfo info;
|
||||
info.res = result;
|
||||
setErrorContext(ErrorTypes::RING_BUF_ERROR, info);
|
||||
return result;
|
||||
}
|
||||
bool stxFound = false;
|
||||
size_t stxIdx = 0;
|
||||
for (size_t vectorIdx = 0; vectorIdx < availableData; vectorIdx++) {
|
||||
// handle STX char
|
||||
if (encodedBuf.first[vectorIdx] == DleEncoder::STX_CHAR) {
|
||||
if (not stxFound) {
|
||||
stxFound = true;
|
||||
stxIdx = vectorIdx;
|
||||
} else {
|
||||
// might be lost packet, so we should advance the read pointer
|
||||
// without skipping the STX
|
||||
readSize = vectorIdx;
|
||||
ErrorInfo info;
|
||||
setErrorContext(ErrorTypes::CONSECUTIVE_STX_CHARS, info);
|
||||
return POSSIBLE_PACKET_LOSS;
|
||||
}
|
||||
}
|
||||
// handle ETX char
|
||||
if (encodedBuf.first[vectorIdx] == DleEncoder::ETX_CHAR) {
|
||||
if (stxFound) {
|
||||
// This is propably a packet, so we decode it.
|
||||
size_t decodedLen = 0;
|
||||
size_t dummy = 0;
|
||||
|
||||
ReturnValue_t result =
|
||||
decoder.decode(&encodedBuf.first[stxIdx], availableData - stxIdx, &dummy,
|
||||
decodedBuf.first, decodedBuf.second, &decodedLen);
|
||||
if (result == returnvalue::OK) {
|
||||
ctx.setType(ContextType::PACKET_FOUND);
|
||||
ctx.decodedPacket.first = decodedBuf.first;
|
||||
ctx.decodedPacket.second = decodedLen;
|
||||
readSize = ++vectorIdx;
|
||||
return returnvalue::OK;
|
||||
} else {
|
||||
// invalid packet, skip.
|
||||
readSize = ++vectorIdx;
|
||||
ErrorInfo info;
|
||||
info.res = result;
|
||||
setErrorContext(ErrorTypes::DECODE_ERROR, info);
|
||||
return POSSIBLE_PACKET_LOSS;
|
||||
}
|
||||
} else {
|
||||
// might be lost packet, so we should advance the read pointer
|
||||
readSize = ++vectorIdx;
|
||||
ErrorInfo info;
|
||||
info.len = 0;
|
||||
setErrorContext(ErrorTypes::CONSECUTIVE_ETX_CHARS, info);
|
||||
return POSSIBLE_PACKET_LOSS;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO_PACKET_FOUND;
|
||||
}
|
||||
|
||||
void DleParser::defaultFoundPacketHandler(uint8_t* packet, size_t len, void* args) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "DleParserBase::handleFoundPacket: Detected DLE packet with " << len << " bytes"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printInfo("DleParserBase::handleFoundPacket: Detected DLE packet with %d bytes\n", len);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void DleParser::defaultErrorHandler() {
|
||||
if (ctx.getType() != DleParser::ContextType::ERROR) {
|
||||
errorPrinter("No error");
|
||||
return;
|
||||
}
|
||||
switch (ctx.error.first) {
|
||||
case (ErrorTypes::NONE): {
|
||||
errorPrinter("No error");
|
||||
break;
|
||||
}
|
||||
case (ErrorTypes::DECODE_ERROR): {
|
||||
errorPrinter("Decode Error");
|
||||
break;
|
||||
}
|
||||
case (ErrorTypes::RING_BUF_ERROR): {
|
||||
errorPrinter("Ring Buffer Error");
|
||||
break;
|
||||
}
|
||||
case (ErrorTypes::ENCODED_BUF_TOO_SMALL):
|
||||
case (ErrorTypes::DECODING_BUF_TOO_SMALL): {
|
||||
char opt[64];
|
||||
snprintf(opt, sizeof(opt), ": Too small for packet with length %zu",
|
||||
ctx.decodedPacket.second);
|
||||
if (ctx.error.first == ErrorTypes::ENCODED_BUF_TOO_SMALL) {
|
||||
errorPrinter("Encoded buf too small", opt);
|
||||
} else {
|
||||
errorPrinter("Decoding buf too small", opt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (ErrorTypes::CONSECUTIVE_STX_CHARS): {
|
||||
errorPrinter("Consecutive STX chars detected");
|
||||
break;
|
||||
}
|
||||
case (ErrorTypes::CONSECUTIVE_ETX_CHARS): {
|
||||
errorPrinter("Consecutive ETX chars detected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DleParser::errorPrinter(const char* str, const char* opt) {
|
||||
if (opt == nullptr) {
|
||||
opt = "";
|
||||
}
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::info << "DleParserBase::handleParseError: " << str << opt << std::endl;
|
||||
#else
|
||||
sif::printInfo("DleParserBase::handleParseError: %s%s\n", str, opt);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void DleParser::setErrorContext(ErrorTypes err, ErrorInfo info) {
|
||||
ctx.setType(ContextType::ERROR);
|
||||
ctx.error.first = err;
|
||||
ctx.error.second = info;
|
||||
}
|
||||
|
||||
ReturnValue_t DleParser::confirmBytesRead(size_t bytesRead) {
|
||||
return decodeRingBuf.deleteData(bytesRead);
|
||||
}
|
||||
|
||||
const DleParser::Context& DleParser::getContext() { return ctx; }
|
||||
|
||||
void DleParser::reset() { decodeRingBuf.clear(); }
|
127
src/fsfw/globalfunctions/DleParser.h
Normal file
127
src/fsfw/globalfunctions/DleParser.h
Normal file
@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/container/SimpleRingBuffer.h>
|
||||
#include <fsfw/globalfunctions/DleEncoder.h>
|
||||
#include <fsfw/returnvalues/returnvalue.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
/**
|
||||
* @brief This base helper class can be used to extract DLE encoded packets from a data stream
|
||||
* @details
|
||||
* The core API of the parser takes received packets which can contains DLE packets. The parser
|
||||
* can deal with DLE packets split across multiple packets. It does so by using a dedicated
|
||||
* decoding ring buffer. The user can process received packets and detect errors by
|
||||
* overriding two provided virtual methods. This also allows detecting multiple DLE packets
|
||||
* inside one passed packet.
|
||||
*/
|
||||
class DleParser {
|
||||
public:
|
||||
static constexpr ReturnValue_t NO_PACKET_FOUND = returnvalue::makeCode(1, 1);
|
||||
static constexpr ReturnValue_t POSSIBLE_PACKET_LOSS = returnvalue::makeCode(1, 2);
|
||||
using BufPair = std::pair<uint8_t*, size_t>;
|
||||
|
||||
enum class ContextType { NONE, PACKET_FOUND, ERROR };
|
||||
|
||||
enum class ErrorTypes {
|
||||
NONE,
|
||||
ENCODED_BUF_TOO_SMALL,
|
||||
DECODING_BUF_TOO_SMALL,
|
||||
DECODE_ERROR,
|
||||
RING_BUF_ERROR,
|
||||
CONSECUTIVE_STX_CHARS,
|
||||
CONSECUTIVE_ETX_CHARS
|
||||
};
|
||||
|
||||
union ErrorInfo {
|
||||
size_t len;
|
||||
ReturnValue_t res;
|
||||
};
|
||||
|
||||
using ErrorPair = std::pair<ErrorTypes, ErrorInfo>;
|
||||
|
||||
struct Context {
|
||||
public:
|
||||
Context() { setType(ContextType::PACKET_FOUND); }
|
||||
|
||||
void setType(ContextType type) {
|
||||
this->type = type;
|
||||
if (type == ContextType::PACKET_FOUND) {
|
||||
error.first = ErrorTypes::NONE;
|
||||
error.second.len = 0;
|
||||
} else {
|
||||
decodedPacket.first = nullptr;
|
||||
decodedPacket.second = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ContextType getType() const { return type; }
|
||||
|
||||
BufPair decodedPacket = {};
|
||||
ErrorPair error;
|
||||
|
||||
private:
|
||||
ContextType type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class constructor
|
||||
* @param decodeRingBuf Ring buffer used to store multiple packets to allow detecting DLE packets
|
||||
* split across multiple packets
|
||||
* @param decoder Decoder instance
|
||||
* @param encodedBuf Buffer used to store encoded packets. It has to be large enough to hold
|
||||
* the largest expected encoded DLE packet size
|
||||
* @param decodedBuf Buffer used to store decoded packets. It has to be large enough to hold the
|
||||
* largest expected decoded DLE packet size
|
||||
* @param handler Function which will be called on a found packet
|
||||
* @param args Arbitrary user argument
|
||||
*/
|
||||
DleParser(SimpleRingBuffer& decodeRingBuf, DleEncoder& decoder, BufPair encodedBuf,
|
||||
BufPair decodedBuf);
|
||||
|
||||
/**
|
||||
* This function allows to pass new data into the parser. It then scans for DLE packets
|
||||
* automatically and inserts (part of) the packet into a ring buffer if necessary.
|
||||
* @param data
|
||||
* @param len
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t passData(const uint8_t* data, size_t len);
|
||||
|
||||
ReturnValue_t parseRingBuf(size_t& bytesRead);
|
||||
|
||||
ReturnValue_t confirmBytesRead(size_t bytesRead);
|
||||
|
||||
const Context& getContext();
|
||||
/**
|
||||
* Example found packet handler
|
||||
* function call
|
||||
* @param packet Decoded packet
|
||||
* @param len Length of detected packet
|
||||
*/
|
||||
void defaultFoundPacketHandler(uint8_t* packet, size_t len, void* args);
|
||||
/**
|
||||
* Will be called if an error occured in the #passData call
|
||||
* @param err
|
||||
* @param ctx Context information depending on the error type
|
||||
* - For buffer length errors, will be set to the detected packet length which is too large
|
||||
* - For decode or ring buffer errors, will be set to the result returned from the failed call
|
||||
*/
|
||||
void defaultErrorHandler();
|
||||
|
||||
static void errorPrinter(const char* str, const char* opt = nullptr);
|
||||
|
||||
void setErrorContext(ErrorTypes err, ErrorInfo ctx);
|
||||
/**
|
||||
* Resets the parser by resetting the internal states and clearing the decoding ring buffer
|
||||
*/
|
||||
void reset();
|
||||
|
||||
private:
|
||||
SimpleRingBuffer& decodeRingBuf;
|
||||
DleEncoder& decoder;
|
||||
BufPair encodedBuf;
|
||||
BufPair decodedBuf;
|
||||
Context ctx;
|
||||
};
|
@ -41,6 +41,8 @@ class Service11TelecommandScheduling final : public PusServiceBase {
|
||||
static constexpr ReturnValue_t INVALID_TIME_WINDOW = returnvalue::makeCode(CLASS_ID, 2);
|
||||
static constexpr ReturnValue_t TIMESHIFTING_NOT_POSSIBLE = returnvalue::makeCode(CLASS_ID, 3);
|
||||
static constexpr ReturnValue_t INVALID_RELATIVE_TIME = returnvalue::makeCode(CLASS_ID, 4);
|
||||
static constexpr ReturnValue_t CONTAINED_TC_TOO_SMALL = returnvalue::makeCode(CLASS_ID, 5);
|
||||
static constexpr ReturnValue_t CONTAINED_TC_CRC_MISSMATCH = returnvalue::makeCode(CLASS_ID, 6);
|
||||
|
||||
static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PUS_SERVICE_11;
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include "fsfw/serialize/SerializeAdapter.h"
|
||||
#include "fsfw/serviceinterface.h"
|
||||
#include "fsfw/tmtcservices/AcceptsTelecommandsIF.h"
|
||||
#include "fsfw/tmtcpacket/pus/tc/PusTcIF.h"
|
||||
#include "fsfw/globalfunctions/CRC.h"
|
||||
|
||||
static constexpr auto DEF_END = SerializeIF::Endianness::BIG;
|
||||
|
||||
@ -171,6 +173,14 @@ inline ReturnValue_t Service11TelecommandScheduling<MAX_NUM_TCS>::doInsertActivi
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
if (size < PusTcIF::MIN_SIZE) {
|
||||
return CONTAINED_TC_TOO_SMALL;
|
||||
}
|
||||
|
||||
if (CRC::crc16ccitt(data, size) != 0) {
|
||||
return CONTAINED_TC_CRC_MISSMATCH;
|
||||
}
|
||||
|
||||
// store currentPacket and receive the store address
|
||||
store_address_t addr{};
|
||||
if (tcStore->addData(&addr, data, size) != returnvalue::OK ||
|
||||
|
@ -5,20 +5,28 @@
|
||||
|
||||
class SourceSequenceCounter {
|
||||
private:
|
||||
uint16_t sequenceCount;
|
||||
uint16_t sequenceCount = 0;
|
||||
|
||||
public:
|
||||
SourceSequenceCounter() : sequenceCount(0) {}
|
||||
void increment() {
|
||||
sequenceCount = (sequenceCount + 1) % (SpacePacketBase::LIMIT_SEQUENCE_COUNT);
|
||||
SourceSequenceCounter(uint16_t initialSequenceCount = 0) : sequenceCount(initialSequenceCount) {}
|
||||
void increment() { sequenceCount = (sequenceCount + 1) % (ccsds::LIMIT_SEQUENCE_COUNT); }
|
||||
void decrement() { sequenceCount = (sequenceCount - 1) % (ccsds::LIMIT_SEQUENCE_COUNT); }
|
||||
uint16_t get() const { return this->sequenceCount; }
|
||||
void reset(uint16_t toValue = 0) { sequenceCount = toValue % (ccsds::LIMIT_SEQUENCE_COUNT); }
|
||||
SourceSequenceCounter& operator++(int) {
|
||||
this->increment();
|
||||
return *this;
|
||||
}
|
||||
void decrement() {
|
||||
sequenceCount = (sequenceCount - 1) % (SpacePacketBase::LIMIT_SEQUENCE_COUNT);
|
||||
SourceSequenceCounter& operator--(int) {
|
||||
this->decrement();
|
||||
return *this;
|
||||
}
|
||||
uint16_t get() { return this->sequenceCount; }
|
||||
void reset(uint16_t toValue = 0) {
|
||||
sequenceCount = toValue % (SpacePacketBase::LIMIT_SEQUENCE_COUNT);
|
||||
SourceSequenceCounter& operator=(const uint16_t& newCount) {
|
||||
sequenceCount = newCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator uint16_t() { return this->get(); }
|
||||
};
|
||||
|
||||
#endif /* FSFW_TMTCSERVICES_SOURCESEQUENCECOUNTER_H_ */
|
||||
|
@ -32,6 +32,8 @@ ReturnValue_t CommandExecutor::execute() {
|
||||
} else if (state == States::PENDING) {
|
||||
return COMMAND_PENDING;
|
||||
}
|
||||
// Reset data in read vector
|
||||
std::memset(readVec.data(), 0, readVec.size());
|
||||
currentCmdFile = popen(currentCmd.c_str(), "r");
|
||||
if (currentCmdFile == nullptr) {
|
||||
lastError = errno;
|
||||
@ -205,3 +207,5 @@ ReturnValue_t CommandExecutor::executeBlocking() {
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
const std::vector<char>& CommandExecutor::getReadVector() const { return readVec; }
|
||||
|
@ -107,6 +107,8 @@ class CommandExecutor {
|
||||
*/
|
||||
void reset();
|
||||
|
||||
const std::vector<char>& getReadVector() const;
|
||||
|
||||
private:
|
||||
std::string currentCmd;
|
||||
bool blocking = true;
|
||||
|
@ -168,12 +168,18 @@ ReturnValue_t I2cComIF::requestReceiveMessage(CookieIF* cookie, size_t requestLe
|
||||
|
||||
int readLen = read(fd, replyBuffer, requestLen);
|
||||
if (readLen != static_cast<int>(requestLen)) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1 and FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "I2cComIF::requestReceiveMessage: Reading from I2C "
|
||||
<< "device failed with error code " << errno << ". Description"
|
||||
<< " of error: " << strerror(errno) << std::endl;
|
||||
sif::error << "I2cComIF::requestReceiveMessage: Read only " << readLen << " from " << requestLen
|
||||
<< " bytes" << std::endl;
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
if (readLen < 0) {
|
||||
sif::warning << "I2cComIF::requestReceiveMessage: Reading from I2C "
|
||||
<< "device failed with error code " << errno << " | " << strerror(errno)
|
||||
<< std::endl;
|
||||
} else {
|
||||
sif::warning << "I2cComIF::requestReceiveMessage: Read only " << readLen << " from "
|
||||
<< requestLen << " bytes" << std::endl;
|
||||
}
|
||||
#else
|
||||
#endif
|
||||
#endif
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::debug << "I2cComIF::requestReceiveMessage: Read " << readLen << " of " << requestLen
|
||||
|
@ -1,2 +1,3 @@
|
||||
target_sources(${FSFW_TEST_TGT} PRIVATE testDistributor.cpp
|
||||
testFaultHandler.cpp)
|
||||
target_sources(
|
||||
${FSFW_TEST_TGT} PRIVATE testDistributor.cpp testDestHandler.cpp
|
||||
testSourceHandler.cpp testFaultHandler.cpp)
|
||||
|
244
unittests/cfdp/handler/testDestHandler.cpp
Normal file
244
unittests/cfdp/handler/testDestHandler.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
#include <etl/crc32.h>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <random>
|
||||
#include <utility>
|
||||
|
||||
#include "fsfw/cfdp.h"
|
||||
#include "fsfw/cfdp/pdu/EofPduCreator.h"
|
||||
#include "fsfw/cfdp/pdu/FileDataCreator.h"
|
||||
#include "fsfw/cfdp/pdu/MetadataPduCreator.h"
|
||||
#include "mocks/AcceptsTmMock.h"
|
||||
#include "mocks/EventReportingProxyMock.h"
|
||||
#include "mocks/FilesystemMock.h"
|
||||
#include "mocks/MessageQueueMock.h"
|
||||
#include "mocks/StorageManagerMock.h"
|
||||
#include "mocks/cfdp/FaultHandlerMock.h"
|
||||
#include "mocks/cfdp/RemoteConfigTableMock.h"
|
||||
#include "mocks/cfdp/UserMock.h"
|
||||
|
||||
TEST_CASE("CFDP Dest Handler", "[cfdp]") {
|
||||
using namespace cfdp;
|
||||
using namespace returnvalue;
|
||||
MessageQueueId_t destQueueId = 2;
|
||||
AcceptsTmMock tmReceiver(destQueueId);
|
||||
MessageQueueMock mqMock(destQueueId);
|
||||
EntityId localId = EntityId(UnsignedByteField<uint16_t>(2));
|
||||
EntityId remoteId = EntityId(UnsignedByteField<uint16_t>(3));
|
||||
FaultHandlerMock fhMock;
|
||||
LocalEntityCfg localEntityCfg(localId, IndicationCfg(), fhMock);
|
||||
FilesystemMock fsMock;
|
||||
UserMock userMock(fsMock);
|
||||
RemoteConfigTableMock remoteCfgTableMock;
|
||||
PacketInfoList<64> packetInfoList;
|
||||
LostSegmentsList<128> lostSegmentsList;
|
||||
DestHandlerParams dp(localEntityCfg, userMock, remoteCfgTableMock, packetInfoList,
|
||||
lostSegmentsList);
|
||||
EventReportingProxyMock eventReporterMock;
|
||||
LocalPool::LocalPoolConfig storeCfg = {{10, 32}, {10, 64}, {10, 128}, {10, 1024}};
|
||||
StorageManagerMock tcStore(2, storeCfg);
|
||||
StorageManagerMock tmStore(3, storeCfg);
|
||||
FsfwParams fp(tmReceiver, &mqMock, &eventReporterMock);
|
||||
RemoteEntityCfg cfg(remoteId);
|
||||
remoteCfgTableMock.addRemoteConfig(cfg);
|
||||
fp.tcStore = &tcStore;
|
||||
fp.tmStore = &tmStore;
|
||||
uint8_t* buf = nullptr;
|
||||
size_t serLen = 0;
|
||||
store_address_t storeId;
|
||||
PduConfig conf;
|
||||
auto destHandler = DestHandler(dp, fp);
|
||||
CHECK(destHandler.initialize() == OK);
|
||||
|
||||
auto metadataPreparation = [&](FileSize cfdpFileSize, ChecksumType checksumType) {
|
||||
std::string srcNameString = "hello.txt";
|
||||
std::string destNameString = "hello-cpy.txt";
|
||||
StringLv srcName(srcNameString);
|
||||
StringLv destName(destNameString);
|
||||
MetadataInfo info(false, checksumType, cfdpFileSize, srcName, destName);
|
||||
TransactionSeqNum seqNum(UnsignedByteField<uint16_t>(1));
|
||||
conf.sourceId = remoteId;
|
||||
conf.destId = localId;
|
||||
conf.mode = TransmissionMode::UNACKNOWLEDGED;
|
||||
conf.seqNum = seqNum;
|
||||
MetadataPduCreator metadataCreator(conf, info);
|
||||
REQUIRE(tcStore.getFreeElement(&storeId, metadataCreator.getSerializedSize(), &buf) == OK);
|
||||
REQUIRE(metadataCreator.serialize(buf, serLen, metadataCreator.getSerializedSize()) == OK);
|
||||
PacketInfo packetInfo(metadataCreator.getPduType(), storeId,
|
||||
metadataCreator.getDirectiveCode());
|
||||
packetInfoList.push_back(packetInfo);
|
||||
};
|
||||
|
||||
auto metadataCheck = [&](const cfdp::DestHandler::FsmResult& res, const char* sourceName,
|
||||
const char* destName, size_t fileLen) {
|
||||
REQUIRE(res.result == OK);
|
||||
REQUIRE(res.callStatus == CallStatus::CALL_AGAIN);
|
||||
REQUIRE(res.errors == 0);
|
||||
// Assert that the packet was deleted after handling
|
||||
REQUIRE(not tcStore.hasDataAtId(storeId));
|
||||
REQUIRE(packetInfoList.empty());
|
||||
REQUIRE(userMock.metadataRecvd.size() == 1);
|
||||
auto& idMetadataPair = userMock.metadataRecvd.back();
|
||||
REQUIRE(idMetadataPair.first == destHandler.getTransactionId());
|
||||
REQUIRE(idMetadataPair.second.sourceId.getValue() == 3);
|
||||
REQUIRE(idMetadataPair.second.fileSize == fileLen);
|
||||
REQUIRE(strcmp(idMetadataPair.second.destFileName, destName) == 0);
|
||||
REQUIRE(strcmp(idMetadataPair.second.sourceFileName, sourceName) == 0);
|
||||
userMock.metadataRecvd.pop();
|
||||
REQUIRE(fsMock.fileMap.find(destName) != fsMock.fileMap.end());
|
||||
REQUIRE(res.result == OK);
|
||||
REQUIRE(res.state == CfdpStates::BUSY_CLASS_1_NACKED);
|
||||
REQUIRE(res.step == DestHandler::TransactionStep::RECEIVING_FILE_DATA_PDUS);
|
||||
};
|
||||
|
||||
auto eofPreparation = [&](FileSize cfdpFileSize, uint32_t crc) {
|
||||
EofInfo eofInfo(cfdp::ConditionCode::NO_ERROR, crc, std::move(cfdpFileSize));
|
||||
EofPduCreator eofCreator(conf, eofInfo);
|
||||
REQUIRE(tcStore.getFreeElement(&storeId, eofCreator.getSerializedSize(), &buf) == OK);
|
||||
REQUIRE(eofCreator.serialize(buf, serLen, eofCreator.getSerializedSize()) == OK);
|
||||
PacketInfo packetInfo(eofCreator.getPduType(), storeId, eofCreator.getDirectiveCode());
|
||||
packetInfoList.push_back(packetInfo);
|
||||
};
|
||||
|
||||
auto eofCheck = [&](const cfdp::DestHandler::FsmResult& res, const TransactionId& id) {
|
||||
REQUIRE(res.result == OK);
|
||||
REQUIRE(res.state == CfdpStates::IDLE);
|
||||
REQUIRE(res.errors == 0);
|
||||
REQUIRE(res.step == DestHandler::TransactionStep::IDLE);
|
||||
// Assert that the packet was deleted after handling
|
||||
REQUIRE(not tcStore.hasDataAtId(storeId));
|
||||
REQUIRE(packetInfoList.empty());
|
||||
REQUIRE(userMock.eofsRevd.size() == 1);
|
||||
auto& eofId = userMock.eofsRevd.back();
|
||||
CHECK(eofId == id);
|
||||
REQUIRE(userMock.finishedRecvd.size() == 1);
|
||||
auto& idParamPair = userMock.finishedRecvd.back();
|
||||
CHECK(idParamPair.first == id);
|
||||
CHECK(idParamPair.second.condCode == ConditionCode::NO_ERROR);
|
||||
};
|
||||
|
||||
auto fileDataPduCheck = [&](const cfdp::DestHandler::FsmResult& res,
|
||||
const std::vector<store_address_t>& idsToCheck) {
|
||||
REQUIRE(res.result == OK);
|
||||
REQUIRE(res.state == CfdpStates::BUSY_CLASS_1_NACKED);
|
||||
REQUIRE(res.step == DestHandler::TransactionStep::RECEIVING_FILE_DATA_PDUS);
|
||||
for (const auto id : idsToCheck) {
|
||||
REQUIRE(not tcStore.hasDataAtId(id));
|
||||
}
|
||||
REQUIRE(packetInfoList.empty());
|
||||
};
|
||||
|
||||
SECTION("State") {
|
||||
CHECK(destHandler.getCfdpState() == CfdpStates::IDLE);
|
||||
CHECK(destHandler.getTransactionStep() == DestHandler::TransactionStep::IDLE);
|
||||
}
|
||||
|
||||
SECTION("Idle State Machine Iteration") {
|
||||
auto res = destHandler.performStateMachine();
|
||||
CHECK(res.result == OK);
|
||||
CHECK(res.callStatus == CallStatus::CALL_AFTER_DELAY);
|
||||
CHECK(res.errors == 0);
|
||||
CHECK(destHandler.getCfdpState() == CfdpStates::IDLE);
|
||||
CHECK(destHandler.getTransactionStep() == DestHandler::TransactionStep::IDLE);
|
||||
}
|
||||
|
||||
SECTION("Empty File Transfer") {
|
||||
const DestHandler::FsmResult& res = destHandler.performStateMachine();
|
||||
CHECK(res.result == OK);
|
||||
FileSize cfdpFileSize(0);
|
||||
metadataPreparation(cfdpFileSize, ChecksumType::NULL_CHECKSUM);
|
||||
destHandler.performStateMachine();
|
||||
metadataCheck(res, "hello.txt", "hello-cpy.txt", 0);
|
||||
destHandler.performStateMachine();
|
||||
REQUIRE(res.callStatus == CallStatus::CALL_AFTER_DELAY);
|
||||
auto transactionId = destHandler.getTransactionId();
|
||||
eofPreparation(cfdpFileSize, 0);
|
||||
// After EOF, operation is done because no closure was requested
|
||||
destHandler.performStateMachine();
|
||||
eofCheck(res, transactionId);
|
||||
}
|
||||
|
||||
SECTION("Small File Transfer") {
|
||||
const DestHandler::FsmResult& res = destHandler.performStateMachine();
|
||||
CHECK(res.result == OK);
|
||||
std::string fileData = "hello test data";
|
||||
etl::crc32 crcCalc;
|
||||
crcCalc.add(fileData.begin(), fileData.end());
|
||||
uint32_t crc32 = crcCalc.value();
|
||||
FileSize cfdpFileSize(fileData.size());
|
||||
metadataPreparation(cfdpFileSize, ChecksumType::CRC_32);
|
||||
destHandler.performStateMachine();
|
||||
metadataCheck(res, "hello.txt", "hello-cpy.txt", fileData.size());
|
||||
destHandler.performStateMachine();
|
||||
REQUIRE(res.callStatus == CallStatus::CALL_AFTER_DELAY);
|
||||
auto transactionId = destHandler.getTransactionId();
|
||||
FileSize offset(0);
|
||||
FileDataInfo fdPduInfo(offset, reinterpret_cast<const uint8_t*>(fileData.data()),
|
||||
fileData.size());
|
||||
FileDataCreator fdPduCreator(conf, fdPduInfo);
|
||||
REQUIRE(tcStore.getFreeElement(&storeId, fdPduCreator.getSerializedSize(), &buf) == OK);
|
||||
REQUIRE(fdPduCreator.serialize(buf, serLen, fdPduCreator.getSerializedSize()) == OK);
|
||||
PacketInfo packetInfo(fdPduCreator.getPduType(), storeId, std::nullopt);
|
||||
packetInfoList.push_back(packetInfo);
|
||||
destHandler.performStateMachine();
|
||||
fileDataPduCheck(res, {storeId});
|
||||
eofPreparation(cfdpFileSize, crc32);
|
||||
// After EOF, operation is done because no closure was requested
|
||||
destHandler.performStateMachine();
|
||||
eofCheck(res, transactionId);
|
||||
}
|
||||
|
||||
SECTION("Segmented File Transfer") {
|
||||
const DestHandler::FsmResult& res = destHandler.performStateMachine();
|
||||
CHECK(res.result == OK);
|
||||
std::random_device dev;
|
||||
std::mt19937 rng(dev());
|
||||
std::uniform_int_distribution<std::mt19937::result_type> distU8(0, 255);
|
||||
std::array<uint8_t, 1024> largerFileData{};
|
||||
for (auto& val : largerFileData) {
|
||||
val = distU8(rng);
|
||||
}
|
||||
etl::crc32 crcCalc;
|
||||
crcCalc.add(largerFileData.begin(), largerFileData.end());
|
||||
uint32_t crc32 = crcCalc.value();
|
||||
FileSize cfdpFileSize(largerFileData.size());
|
||||
metadataPreparation(cfdpFileSize, ChecksumType::CRC_32);
|
||||
destHandler.performStateMachine();
|
||||
metadataCheck(res, "hello.txt", "hello-cpy.txt", largerFileData.size());
|
||||
destHandler.performStateMachine();
|
||||
REQUIRE(res.callStatus == CallStatus::CALL_AFTER_DELAY);
|
||||
auto transactionId = destHandler.getTransactionId();
|
||||
|
||||
std::vector<store_address_t> idsToCheck;
|
||||
{
|
||||
FileSize offset(0);
|
||||
FileDataInfo fdPduInfo(offset, reinterpret_cast<const uint8_t*>(largerFileData.data()),
|
||||
largerFileData.size() / 2);
|
||||
FileDataCreator fdPduCreator(conf, fdPduInfo);
|
||||
REQUIRE(tcStore.getFreeElement(&storeId, fdPduCreator.getSerializedSize(), &buf) == OK);
|
||||
REQUIRE(fdPduCreator.serialize(buf, serLen, fdPduCreator.getSerializedSize()) == OK);
|
||||
PacketInfo packetInfo(fdPduCreator.getPduType(), storeId, std::nullopt);
|
||||
idsToCheck.push_back(storeId);
|
||||
packetInfoList.push_back(packetInfo);
|
||||
}
|
||||
|
||||
{
|
||||
FileSize offset(512);
|
||||
FileDataInfo fdPduInfo(offset, reinterpret_cast<const uint8_t*>(largerFileData.data() + 512),
|
||||
largerFileData.size() / 2);
|
||||
FileDataCreator fdPduCreator(conf, fdPduInfo);
|
||||
REQUIRE(tcStore.getFreeElement(&storeId, fdPduCreator.getSerializedSize(), &buf) == OK);
|
||||
REQUIRE(fdPduCreator.serialize(buf, serLen, fdPduCreator.getSerializedSize()) == OK);
|
||||
PacketInfo packetInfo(fdPduCreator.getPduType(), storeId, std::nullopt);
|
||||
idsToCheck.push_back(storeId);
|
||||
packetInfoList.push_back(packetInfo);
|
||||
}
|
||||
|
||||
destHandler.performStateMachine();
|
||||
fileDataPduCheck(res, idsToCheck);
|
||||
eofPreparation(cfdpFileSize, crc32);
|
||||
// After EOF, operation is done because no closure was requested
|
||||
destHandler.performStateMachine();
|
||||
eofCheck(res, transactionId);
|
||||
}
|
||||
}
|
3
unittests/cfdp/handler/testSourceHandler.cpp
Normal file
3
unittests/cfdp/handler/testSourceHandler.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
TEST_CASE("CFDP Source Handler", "[cfdp]") {}
|
Reference in New Issue
Block a user