#include "SourceHandler.h" #include #include #include "fsfw/cfdp/pdu/EofPduCreator.h" #include "fsfw/cfdp/pdu/FileDataCreator.h" #include "fsfw/cfdp/pdu/MetadataPduCreator.h" #include "fsfw/filesystem/HasFileSystemIF.h" #include "fsfw/objectmanager.h" #include "fsfw/serviceinterface.h" #include "fsfw/tmtcservices/TmTcMessage.h" using namespace returnvalue; cfdp::SourceHandler::SourceHandler(SourceHandlerParams params, FsfwParams fsfwParams) : sourceParams(std::move(params)), fsfwParams(fsfwParams) { // The entity ID portion of the transaction ID will always remain fixed. transactionParams.id.entityId = sourceParams.cfg.localId; if (sourceParams.seqCountProvider.bitWidth() == 8) { transactionParams.seqCountWidth = cfdp::WidthInBytes::ONE_BYTE; } else if (sourceParams.seqCountProvider.bitWidth() == 16) { transactionParams.seqCountWidth = cfdp::WidthInBytes::TWO_BYTES; } else if (sourceParams.seqCountProvider.bitWidth() == 32) { transactionParams.seqCountWidth = cfdp::WidthInBytes::FOUR_BYTES; } else { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "cfdp::SourceHandler: Seq count provider bit width " << sourceParams.seqCountProvider.bitWidth() << " not allowed" << std::endl; #else sif::printError("cfdp::SourceHandler: Seq count provider bit width %d not allowed\n", sourceParams.seqCountProvider.bitWidth()); #endif // Yeah, what am I supposed to do here? Can't throw an exception in the FSFW.. transactionParams.seqCountWidth = cfdp::WidthInBytes::ONE_BYTE; } } cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmNacked() { ReturnValue_t result; if (step == TransactionStep::IDLE) { step = TransactionStep::TRANSACTION_START; } if (step == TransactionStep::TRANSACTION_START) { sourceParams.user.transactionIndication(transactionParams.id); step = TransactionStep::CRC_PROCEDURE; } if (step == TransactionStep::CRC_PROCEDURE) { result = checksumGeneration(); if (result != OK) { // TODO: Some error handling } step = TransactionStep::SENDING_METADATA; } if (step == TransactionStep::SENDING_METADATA) { result = prepareAndSendMetadataPdu(); if (result != OK) { // TODO: Error handling } return fsmResult; } if (step == TransactionStep::SENDING_FILE_DATA) { result = prepareAndSendNextFileDataPdu(); if (result != OK) { // TODO: Error handling } return fsmResult; } if (step == TransactionStep::SENDING_EOF) { result = prepareAndSendEofPdu(); if (result != OK) { // TODO: Error handling } if (sourceParams.cfg.indicCfg.eofSentIndicRequired) { sourceParams.user.eofSentIndication(transactionParams.id); } if (transactionParams.closureRequested) { step = TransactionStep::WAIT_FOR_FINISH; return fsmResult; } step = TransactionStep::NOTICE_OF_COMPLETION; } if (step == TransactionStep::WAIT_FOR_FINISH) { // TODO: In case this is a request with closure, wait for finish. // Done, issue notice of completion step = TransactionStep::NOTICE_OF_COMPLETION; } if (step == TransactionStep::NOTICE_OF_COMPLETION) { noticeOfCompletion(); reset(); } return fsmResult; } cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::stateMachine() { if (state == cfdp::CfdpState::IDLE) { return fsmResult; } if (state == cfdp::CfdpState::BUSY_CLASS_1_NACKED) { return fsmNacked(); } return fsmResult; } ReturnValue_t cfdp::SourceHandler::checksumGeneration() { if (transactionParams.fileSize.value() == 0) { // NULL checksum for empty file. transactionParams.crc = 0; return OK; } std::array buf{}; etl::crc32 crcCalc; uint64_t currentOffset = 0; FileOpParams params(transactionParams.destName.data(), transactionParams.fileSize.value()); while (currentOffset < transactionParams.fileSize.value()) { uint64_t readLen; if (currentOffset + buf.size() > transactionParams.fileSize.value()) { readLen = transactionParams.fileSize.value() - currentOffset; } else { readLen = buf.size(); } if (readLen > 0) { params.offset = currentOffset; params.size = readLen; auto result = sourceParams.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; } transactionParams.crc = crcCalc.value(); return OK; } ReturnValue_t cfdp::SourceHandler::putRequest(PutRequestFull& putRequest, RemoteEntityCfg& cfg) { if (state != CfdpState::IDLE) { return SOURCE_TRANSACTION_PENDING; } if (putRequest.sourceNameSize > transactionParams.sourceName.size()) { return FAILED; } if (putRequest.destNameSize > transactionParams.destName.size()) { return FAILED; } std::memcpy(transactionParams.destName.data(), putRequest.destName, putRequest.destNameSize); std::memcpy(transactionParams.sourceName.data(), putRequest.sourceName, putRequest.sourceNameSize); transactionParams.sourceNameSize = putRequest.sourceNameSize; transactionParams.destNameSize = putRequest.destNameSize; FilesystemParams params(transactionParams.sourceName.data()); if (!sourceParams.user.vfs.fileExists(params)) { return FILE_DOES_NOT_EXIST; } if (cfg.maxFileSegmentLen > fileBuf.size() or cfg.maxFileSegmentLen == 0) { return FILE_SEGMENT_LEN_INVALID; } transactionParams.closureRequested = putRequest.closureRequested; transactionParams.pduConf.mode = putRequest.transmissionMode; transactionParams.pduConf.destId = putRequest.destId; // Only used for PDU forwarding, file is sent to file receiver regularly here. transactionParams.pduConf.direction = Direction::TOWARDS_RECEIVER; transactionParams.pduConf.sourceId = sourceParams.cfg.localId; transactionParams.id.seqNum.setValue(sourceParams.seqCountProvider.getAndIncrement()); if (transactionParams.pduConf.mode == TransmissionMode::ACKNOWLEDGED) { state = cfdp::CfdpState::BUSY_CLASS_2_ACKED; } else if (transactionParams.pduConf.mode == TransmissionMode::UNACKNOWLEDGED) { state = cfdp::CfdpState::BUSY_CLASS_1_NACKED; } step = TransactionStep::IDLE; uint64_t fileSize = 0; sourceParams.user.vfs.getFileSize(params, fileSize); transactionParams.pduConf.largeFile = false; if (fileSize > UINT32_MAX) { transactionParams.pduConf.largeFile = true; } transactionParams.fileSize.setFileSize(fileSize, transactionParams.pduConf.largeFile); transactionParams.remoteCfg = cfg; return OK; } ReturnValue_t cfdp::SourceHandler::prepareAndSendMetadataPdu() { cfdp::StringLv sourceName(transactionParams.sourceName.data(), transactionParams.sourceNameSize); cfdp::StringLv destName(transactionParams.destName.data(), transactionParams.destNameSize); auto metadataInfo = MetadataInfo(transactionParams.fileSize, sourceName, destName); auto metadataPdu = MetadataPduCreator(transactionParams.pduConf, metadataInfo, nullptr, 0); ReturnValue_t result = sendGenericPdu(metadataPdu); if (result != OK) { return result; } // Advance FSM if everything works step = TransactionStep::SENDING_FILE_DATA; return OK; } ReturnValue_t cfdp::SourceHandler::prepareAndSendNextFileDataPdu() { cfdp::Fss offset(transactionParams.progress); uint64_t readLen; uint64_t fileSize = transactionParams.fileSize.value(); if (fileSize == 0) { // We are done, no need to send file data PDUs for an empty file. step = TransactionStep::SENDING_EOF; return OK; } if (fileSize < transactionParams.remoteCfg.maxFileSegmentLen) { readLen = transactionParams.fileSize.value(); } else { if (transactionParams.progress + transactionParams.remoteCfg.maxFileSegmentLen > fileSize) { readLen = fileSize - transactionParams.progress; } else { readLen = transactionParams.remoteCfg.maxFileSegmentLen; } } FileOpParams fileParams(transactionParams.sourceName.data(), readLen); ReturnValue_t result = sourceParams.user.vfs.readFromFile(fileParams, fileBuf.data(), fileBuf.size()); if (result != returnvalue::OK) { return result; } auto fileDataInfo = FileDataInfo(offset, fileBuf.data(), readLen); auto fileDataPdu = FileDataCreator(transactionParams.pduConf, fileDataInfo); result = sendGenericPdu(fileDataPdu); if (result != OK) { return result; } transactionParams.progress += readLen; if (transactionParams.progress >= fileSize) { // Advance FSM after all file data PDUs were sent. step = TransactionStep::SENDING_EOF; } return OK; } ReturnValue_t cfdp::SourceHandler::prepareAndSendEofPdu() { auto eofInfo = EofInfo(ConditionCode::NO_ERROR, transactionParams.crc, transactionParams.fileSize); auto eofPdu = EofPduCreator(transactionParams.pduConf, eofInfo); ReturnValue_t result = sendGenericPdu(eofPdu); if (result != OK) { return result; } return OK; } ReturnValue_t cfdp::SourceHandler::initialize() { if (fsfwParams.tmStore == nullptr) { fsfwParams.tmStore = ObjectManager::instance()->get(objects::TM_STORE); if (fsfwParams.tmStore == nullptr) { return FAILED; } } if (fsfwParams.tcStore == nullptr) { fsfwParams.tcStore = ObjectManager::instance()->get(objects::TC_STORE); if (fsfwParams.tcStore == nullptr) { return FAILED; } } if (fsfwParams.msgQueue == nullptr) { return FAILED; } return OK; } ReturnValue_t cfdp::SourceHandler::sendGenericPdu(const SerializeIF& pdu) const { uint8_t* dataPtr; store_address_t storeId; ReturnValue_t result = fsfwParams.tcStore->getFreeElement(&storeId, pdu.getSerializedSize(), &dataPtr); if (result != OK) { // TODO: Better error handling? return result; } size_t serializedLen = 0; result = pdu.serializeBe(dataPtr, serializedLen, pdu.getSerializedSize()); if (result != OK) { return result; } TmTcMessage tcMsg(storeId); return fsfwParams.msgQueue->sendMessage(fsfwParams.packetDest.getReportReceptionQueue(), &tcMsg); } ReturnValue_t cfdp::SourceHandler::noticeOfCompletion() { if (sourceParams.cfg.indicCfg.transactionFinishedIndicRequired) { cfdp::TransactionFinishedParams params(transactionParams.id, ConditionCode::NO_ERROR, FileDeliveryCode::DATA_COMPLETE, FileDeliveryStatus::RETAINED_IN_FILESTORE); sourceParams.user.transactionFinishedIndication(params); } return OK; } ReturnValue_t cfdp::SourceHandler::reset() { step = TransactionStep::IDLE; state = cfdp::CfdpState::IDLE; transactionParams.reset(); return OK; }