fsfw/src/fsfw/cfdp/handler/SourceHandler.cpp

377 lines
14 KiB
C++
Raw Normal View History

2022-09-14 19:29:43 +02:00
#include "SourceHandler.h"
2023-06-30 11:36:19 +02:00
2023-07-17 09:53:23 +02:00
#include <etl/crc32.h>
2023-07-17 11:37:20 +02:00
2023-07-17 09:53:23 +02:00
#include <array>
2023-07-19 14:02:03 +02:00
#include "fsfw/cfdp/pdu/EofPduCreator.h"
2023-07-19 00:12:14 +02:00
#include "fsfw/cfdp/pdu/FileDataCreator.h"
2023-07-17 16:40:44 +02:00
#include "fsfw/cfdp/pdu/MetadataPduCreator.h"
2023-07-17 11:37:20 +02:00
#include "fsfw/filesystem/HasFileSystemIF.h"
#include "fsfw/globalfunctions/arrayprinter.h"
2023-07-18 09:38:46 +02:00
#include "fsfw/objectmanager.h"
#include "fsfw/serviceinterface.h"
2023-09-06 10:11:09 +02:00
#include "fsfw/tasks/TaskFactory.h"
2023-07-18 09:38:46 +02:00
#include "fsfw/tmtcservices/TmTcMessage.h"
2023-07-17 09:53:23 +02:00
using namespace returnvalue;
2023-07-17 10:25:09 +02:00
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;
2023-08-03 15:13:26 +02:00
transactionParams.pduConf.sourceId = sourceParams.cfg.localId;
if (sourceParams.seqCountProvider.bitWidth() == 8) {
2023-08-03 15:13:26 +02:00
transactionParams.pduConf.seqNum.setWidth(cfdp::WidthInBytes::ONE_BYTE);
} else if (sourceParams.seqCountProvider.bitWidth() == 16) {
2023-08-03 15:13:26 +02:00
transactionParams.pduConf.seqNum.setWidth(cfdp::WidthInBytes::TWO_BYTES);
} else if (sourceParams.seqCountProvider.bitWidth() == 32) {
2023-08-03 15:13:26 +02:00
transactionParams.pduConf.seqNum.setWidth(cfdp::WidthInBytes::FOUR_BYTES);
} else if (sourceParams.seqCountProvider.bitWidth() == 64) {
transactionParams.pduConf.seqNum.setWidth(cfdp::WidthInBytes::EIGHT_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
2023-07-19 23:43:40 +02:00
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..
2023-08-03 15:13:26 +02:00
transactionParams.pduConf.seqNum.setWidth(cfdp::WidthInBytes::ONE_BYTE);
}
transactionParams.pduConf.seqNum.setValue(0);
}
2023-07-17 09:53:23 +02:00
2023-07-17 15:21:22 +02:00
cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmNacked() {
2023-07-17 15:11:51 +02:00
ReturnValue_t result;
2023-07-14 15:56:38 +02:00
if (step == TransactionStep::IDLE) {
step = TransactionStep::TRANSACTION_START;
}
if (step == TransactionStep::TRANSACTION_START) {
sourceParams.user.transactionIndication(transactionParams.id);
2023-07-17 15:11:51 +02:00
result = checksumGeneration();
2023-07-17 09:53:23 +02:00
if (result != OK) {
2023-10-19 10:21:18 +02:00
addError(result);
2023-07-17 09:53:23 +02:00
}
2023-07-17 15:03:08 +02:00
step = TransactionStep::SENDING_METADATA;
2023-07-14 15:56:38 +02:00
}
if (step == TransactionStep::SENDING_METADATA) {
2023-07-17 15:11:51 +02:00
result = prepareAndSendMetadataPdu();
if (result != OK) {
2023-10-19 10:21:18 +02:00
addError(result);
2023-07-17 15:11:51 +02:00
}
2023-08-14 20:47:27 +02:00
fsmResult.callStatus = CallStatus::CALL_AGAIN;
2023-07-17 15:21:22 +02:00
return fsmResult;
2023-07-14 15:56:38 +02:00
}
if (step == TransactionStep::SENDING_FILE_DATA) {
2023-08-03 15:49:23 +02:00
bool noFdPdu = false;
result = prepareAndSendNextFileDataPdu(noFdPdu);
2023-09-06 10:11:09 +02:00
if (result == OK and !noFdPdu) {
2023-08-14 20:47:27 +02:00
fsmResult.callStatus = CallStatus::CALL_AGAIN;
2023-10-19 10:21:18 +02:00
return fsmResult;
2023-08-03 15:49:23 +02:00
}
2023-07-14 15:56:38 +02:00
}
if (step == TransactionStep::SENDING_EOF) {
2023-07-17 15:21:22 +02:00
result = prepareAndSendEofPdu();
2023-07-17 15:11:51 +02:00
if (result != OK) {
2023-10-19 10:21:18 +02:00
addError(result);
2023-07-17 15:11:51 +02:00
}
2023-07-19 23:43:40 +02:00
if (sourceParams.cfg.indicCfg.eofSentIndicRequired) {
sourceParams.user.eofSentIndication(transactionParams.id);
}
if (transactionParams.closureRequested) {
step = TransactionStep::WAIT_FOR_FINISH;
2023-08-14 20:47:27 +02:00
fsmResult.callStatus = CallStatus::CALL_AFTER_DELAY;
} else {
step = TransactionStep::NOTICE_OF_COMPLETION;
fsmResult.callStatus = CallStatus::CALL_AGAIN;
2023-07-19 23:43:40 +02:00
}
2023-08-14 20:47:27 +02:00
return fsmResult;
2023-07-14 15:56:38 +02:00
}
if (step == TransactionStep::WAIT_FOR_FINISH) {
// TODO: In case this is a request with closure, wait for finish.
2023-07-17 15:11:51 +02:00
// Done, issue notice of completion
step = TransactionStep::NOTICE_OF_COMPLETION;
2023-07-14 15:56:38 +02:00
}
if (step == TransactionStep::NOTICE_OF_COMPLETION) {
2023-07-19 23:43:40 +02:00
noticeOfCompletion();
reset();
2023-07-14 15:56:38 +02:00
}
2023-07-17 15:21:22 +02:00
return fsmResult;
2023-07-14 15:56:38 +02:00
}
2023-07-17 15:21:22 +02:00
2023-08-14 20:47:27 +02:00
const cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::stateMachine() {
2023-08-03 15:49:23 +02:00
fsmResult.packetsSent = 0;
2023-08-14 13:19:48 +02:00
fsmResult.errors = 0;
2023-08-14 20:47:27 +02:00
fsmResult.callStatus = CallStatus::DONE;
2023-07-14 15:56:38 +02:00
if (state == cfdp::CfdpState::IDLE) {
2023-07-17 15:21:22 +02:00
return fsmResult;
2023-07-14 15:56:38 +02:00
}
if (state == cfdp::CfdpState::BUSY_CLASS_1_NACKED) {
return fsmNacked();
}
2023-07-17 15:21:22 +02:00
return fsmResult;
2023-07-14 15:56:38 +02:00
}
2023-07-17 09:53:23 +02:00
ReturnValue_t cfdp::SourceHandler::checksumGeneration() {
2023-07-19 23:43:40 +02:00
if (transactionParams.fileSize.value() == 0) {
// NULL checksum for empty file.
transactionParams.crc = 0;
return OK;
}
2023-07-17 09:53:23 +02:00
std::array<uint8_t, 1024> buf{};
etl::crc32 crcCalc;
uint64_t currentOffset = 0;
2023-08-04 15:17:53 +02:00
FileOpParams params(transactionParams.sourceName.data(), transactionParams.fileSize.value());
2023-07-17 09:53:23 +02:00
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) {
2023-10-19 10:21:18 +02:00
addError(result);
2023-07-17 09:53:23 +02:00
return FAILED;
}
crcCalc.add(buf.begin(), buf.begin() + readLen);
}
currentOffset += readLen;
}
transactionParams.crc = crcCalc.value();
return OK;
}
2023-07-17 10:15:06 +02:00
2023-07-27 17:15:01 +02:00
ReturnValue_t cfdp::SourceHandler::transactionStart(PutRequest& putRequest, RemoteEntityCfg& cfg) {
2023-07-17 15:03:08 +02:00
if (state != CfdpState::IDLE) {
return SOURCE_TRANSACTION_PENDING;
}
2023-07-27 17:15:01 +02:00
if (cfg.remoteId != putRequest.getDestId()) {
return WRONG_REMOTE_CFG_ENTITY_ID;
}
2023-07-27 17:15:01 +02:00
if (putRequest.getSourceName().getValueLen() == 0) {
return SOURCE_NAME_EMPTY;
}
if (putRequest.getDestName().getValueLen() == 0) {
return DEST_NAME_EMPTY;
2023-07-18 09:38:46 +02:00
}
2023-08-03 15:30:38 +02:00
const char* srcNamePtr = putRequest.getSourceName().getCString(transactionParams.sourceNameSize);
const char* destNamePtr = putRequest.getDestName().getCString(transactionParams.destNameSize);
2023-07-27 17:15:01 +02:00
std::strncpy(transactionParams.sourceName.data(), srcNamePtr, transactionParams.sourceNameSize);
std::strncpy(transactionParams.destName.data(), destNamePtr, transactionParams.destNameSize);
2023-09-14 15:04:54 +02:00
// Add 0 termination. The source and dest name size can not be larger than UINT8_MAX, so this
// operation is safe.
transactionParams.sourceName[transactionParams.sourceNameSize] = '\0';
transactionParams.destName[transactionParams.destNameSize] = '\0';
2023-07-17 16:40:44 +02:00
FilesystemParams params(transactionParams.sourceName.data());
if (!sourceParams.user.vfs.fileExists(params)) {
return FILE_DOES_NOT_EXIST;
}
2023-07-19 13:44:52 +02:00
if (cfg.maxFileSegmentLen > fileBuf.size() or cfg.maxFileSegmentLen == 0) {
return FILE_SEGMENT_LEN_INVALID;
}
2023-07-27 17:15:01 +02:00
// If transmission mode is not set, use default transmission mode for the remote entity.
if (not putRequest.getTransmissionMode(transactionParams.pduConf.mode)) {
transactionParams.pduConf.mode = cfg.defaultTransmissionMode;
}
// If closure request field is not set, use default option for the remote entity.
if (not putRequest.getClosureRequested(transactionParams.closureRequested)) {
transactionParams.closureRequested = cfg.closureRequested;
}
2023-08-03 15:13:26 +02:00
const EntityId& destId = putRequest.getDestId();
2023-08-03 15:30:38 +02:00
// The width of the source and destination ID must be the same. Use the larger ID value to
// ensure the width is large enough for both IDs
if (destId.getWidth() > transactionParams.pduConf.sourceId.getWidth()) {
transactionParams.pduConf.destId = destId;
transactionParams.pduConf.sourceId.setWidth(destId.getWidth());
} else {
transactionParams.pduConf.destId.setValueAndWidth(transactionParams.pduConf.sourceId.getWidth(),
destId.getValue());
}
2023-07-18 09:38:46 +02:00
// Only used for PDU forwarding, file is sent to file receiver regularly here.
transactionParams.pduConf.direction = Direction::TOWARDS_RECEIVER;
transactionParams.pduConf.seqNum.setValue(sourceParams.seqCountProvider.getAndIncrement());
transactionParams.id.seqNum = transactionParams.pduConf.seqNum;
2023-07-18 09:38:46 +02:00
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;
2023-07-19 13:44:52 +02:00
uint64_t fileSize = 0;
2023-07-17 16:40:44 +02:00
sourceParams.user.vfs.getFileSize(params, fileSize);
2023-07-18 09:38:46 +02:00
transactionParams.pduConf.largeFile = false;
if (fileSize > UINT32_MAX) {
transactionParams.pduConf.largeFile = true;
}
2023-08-03 17:43:03 +02:00
if (fileSize == 0) {
transactionParams.checksumType = ChecksumType::NULL_CHECKSUM;
} else {
transactionParams.checksumType = ChecksumType::CRC_32;
}
2023-07-18 09:38:46 +02:00
transactionParams.fileSize.setFileSize(fileSize, transactionParams.pduConf.largeFile);
transactionParams.progress = 0;
2023-07-19 13:44:52 +02:00
transactionParams.remoteCfg = cfg;
2023-07-17 14:54:45 +02:00
return OK;
2023-07-17 13:43:48 +02:00
}
2023-07-17 15:11:51 +02:00
ReturnValue_t cfdp::SourceHandler::prepareAndSendMetadataPdu() {
2023-07-18 09:38:46 +02:00
cfdp::StringLv sourceName(transactionParams.sourceName.data(), transactionParams.sourceNameSize);
2023-07-19 13:44:52 +02:00
cfdp::StringLv destName(transactionParams.destName.data(), transactionParams.destNameSize);
2023-08-03 17:43:03 +02:00
auto metadataInfo =
MetadataGenericInfo(transactionParams.closureRequested, transactionParams.checksumType,
transactionParams.fileSize);
2023-07-27 14:55:46 +02:00
auto metadataPdu =
MetadataPduCreator(transactionParams.pduConf, metadataInfo, sourceName, destName, nullptr, 0);
2023-07-19 14:06:15 +02:00
ReturnValue_t result = sendGenericPdu(metadataPdu);
if (result != OK) {
return result;
}
2023-07-18 09:38:46 +02:00
// Advance FSM if everything works
2023-07-17 15:11:51 +02:00
step = TransactionStep::SENDING_FILE_DATA;
return OK;
}
2023-07-17 15:21:22 +02:00
2023-08-03 15:49:23 +02:00
ReturnValue_t cfdp::SourceHandler::prepareAndSendNextFileDataPdu(bool& noFileDataPdu) {
2023-07-19 13:44:52 +02:00
cfdp::Fss offset(transactionParams.progress);
uint64_t readLen;
uint64_t fileSize = transactionParams.fileSize.value();
2023-08-03 15:49:23 +02:00
noFileDataPdu = false;
2023-07-19 13:44:52 +02:00
if (fileSize == 0) {
// We are done, no need to send file data PDUs for an empty file.
step = TransactionStep::SENDING_EOF;
2023-08-03 15:49:23 +02:00
noFileDataPdu = true;
2023-07-19 13:44:52 +02:00
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);
fileParams.offset = transactionParams.progress;
2023-07-19 13:44:52 +02:00
ReturnValue_t result =
sourceParams.user.vfs.readFromFile(fileParams, fileBuf.data(), fileBuf.size());
if (result != returnvalue::OK) {
addError(result);
2023-07-19 13:44:52 +02:00
return result;
}
auto fileDataInfo = FileDataInfo(offset, fileBuf.data(), readLen);
auto fileDataPdu = FileDataCreator(transactionParams.pduConf, fileDataInfo);
2023-07-19 14:06:15 +02:00
result = sendGenericPdu(fileDataPdu);
if (result != OK) {
return result;
}
2023-07-19 13:44:52 +02:00
transactionParams.progress += readLen;
if (transactionParams.progress >= fileSize) {
// Advance FSM after all file data PDUs were sent.
step = TransactionStep::SENDING_EOF;
}
2023-07-17 15:11:51 +02:00
return OK;
}
2023-07-17 15:21:22 +02:00
ReturnValue_t cfdp::SourceHandler::prepareAndSendEofPdu() {
2023-07-19 23:43:40 +02:00
auto eofInfo =
EofInfo(ConditionCode::NO_ERROR, transactionParams.crc, transactionParams.fileSize);
2023-07-19 14:06:15 +02:00
auto eofPdu = EofPduCreator(transactionParams.pduConf, eofInfo);
ReturnValue_t result = sendGenericPdu(eofPdu);
if (result != OK) {
return result;
}
2023-07-17 15:11:51 +02:00
return OK;
}
2023-07-18 09:38:46 +02:00
ReturnValue_t cfdp::SourceHandler::initialize() {
if (fsfwParams.tmStore == nullptr) {
fsfwParams.tmStore = ObjectManager::instance()->get<StorageManagerIF>(objects::TM_STORE);
if (fsfwParams.tmStore == nullptr) {
return FAILED;
}
}
if (fsfwParams.tcStore == nullptr) {
fsfwParams.tcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::TC_STORE);
if (fsfwParams.tcStore == nullptr) {
return FAILED;
}
}
if (fsfwParams.msgQueue == nullptr) {
return FAILED;
}
return OK;
}
2023-07-19 13:44:52 +02:00
2023-08-03 15:49:23 +02:00
ReturnValue_t cfdp::SourceHandler::sendGenericPdu(const SerializeIF& pdu) {
2023-07-19 13:44:52 +02:00
uint8_t* dataPtr;
store_address_t storeId;
ReturnValue_t result =
2023-08-03 13:57:55 +02:00
fsfwParams.tmStore->getFreeElement(&storeId, pdu.getSerializedSize(), &dataPtr);
2023-07-19 13:44:52 +02:00
if (result != OK) {
addError(result);
2023-09-06 10:11:09 +02:00
fsmResult.callStatus = CallStatus::CALL_AFTER_DELAY;
2023-09-06 11:45:02 +02:00
if (result == StorageManagerIF::DATA_STORAGE_FULL) {
2023-09-06 10:11:09 +02:00
return TM_STORE_FULL;
}
2023-07-19 13:44:52 +02:00
return result;
}
size_t serializedLen = 0;
result = pdu.serializeBe(dataPtr, serializedLen, pdu.getSerializedSize());
if (result != OK) {
addError(result);
2023-07-19 13:44:52 +02:00
return result;
}
2023-08-03 13:57:55 +02:00
TmTcMessage tmMsg(storeId);
2023-08-03 15:49:23 +02:00
result =
fsfwParams.msgQueue->sendMessage(fsfwParams.packetDest.getReportReceptionQueue(), &tmMsg);
2023-09-06 11:45:02 +02:00
if (result != OK) {
fsmResult.callStatus = CallStatus::CALL_AFTER_DELAY;
2023-09-06 10:11:09 +02:00
}
if (result == MessageQueueIF::FULL) {
return TARGET_MSG_QUEUE_FULL;
} else if (result == OK) {
2023-08-03 15:49:23 +02:00
fsmResult.packetsSent += 1;
}
return result;
2023-07-19 13:44:52 +02:00
}
2023-07-19 23:43:40 +02:00
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;
2023-08-14 20:47:27 +02:00
fsmResult.callStatus = CallStatus::DONE;
2023-07-19 23:43:40 +02:00
transactionParams.reset();
return OK;
}
2023-07-27 17:15:01 +02:00
cfdp::CfdpState cfdp::SourceHandler::getState() const { return state; }
cfdp::SourceHandler::TransactionStep cfdp::SourceHandler::getStep() const { return step; }
void cfdp::SourceHandler::addError(ReturnValue_t error) {
2023-08-14 12:19:55 +02:00
if (fsmResult.errors < fsmResult.errorCodes.size()) {
fsmResult.errorCodes[fsmResult.errors] = error;
fsmResult.errors++;
2023-09-06 20:49:53 +02:00
fsmResult.result = error;
}
}