258 lines
8.7 KiB
C++
258 lines
8.7 KiB
C++
#include "SourceHandler.h"
|
|
|
|
#include <etl/crc32.h>
|
|
|
|
#include <array>
|
|
|
|
#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/tmtcservices/TmTcMessage.h"
|
|
|
|
using namespace returnvalue;
|
|
|
|
cfdp::SourceHandler::SourceHandler(SourceHandlerParams params, FsfwParams fsfwParams)
|
|
: sourceParams(std::move(params)), fsfwParams(fsfwParams) {}
|
|
|
|
cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmNacked() {
|
|
ReturnValue_t result;
|
|
if (step == TransactionStep::IDLE) {
|
|
step = TransactionStep::TRANSACTION_START;
|
|
}
|
|
if (step == TransactionStep::TRANSACTION_START) {
|
|
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
|
|
}
|
|
return fsmResult;
|
|
}
|
|
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) {
|
|
// TODO: Notice of completion
|
|
// We are done, go back to idle state.
|
|
// TODO: Possible reset state?
|
|
step = TransactionStep::IDLE;
|
|
state = CfdpState::IDLE;
|
|
}
|
|
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() {
|
|
std::array<uint8_t, 1024> 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;
|
|
|
|
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() {
|
|
// TODO: Checksum
|
|
auto eofInfo = EofInfo(ConditionCode::NO_ERROR, 0, transactionParams.fileSize);
|
|
auto eofPdu = EofPduCreator(transactionParams.pduConf, eofInfo);
|
|
ReturnValue_t result = sendGenericPdu(eofPdu);
|
|
if (result != OK) {
|
|
return result;
|
|
}
|
|
step = TransactionStep::WAIT_FOR_FINISH;
|
|
return OK;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|