CFDP Source Handler Testing #803

Merged
muellerr merged 14 commits from cfdp-source-handler-testing into cfdp-source-handler 2023-10-13 17:10:44 +02:00
23 changed files with 216 additions and 119 deletions

View File

@ -1,7 +1,7 @@
/** /**
* @brief Auto-generated event translation file. Contains 314 translations. * @brief Auto-generated event translation file. Contains 314 translations.
* @details * @details
* Generated on: 2023-10-13 09:44:05 * Generated on: 2023-10-13 15:23:30
*/ */
#include "translateEvents.h" #include "translateEvents.h"

View File

@ -2,7 +2,7 @@
* @brief Auto-generated object translation file. * @brief Auto-generated object translation file.
* @details * @details
* Contains 174 translations. * Contains 174 translations.
* Generated on: 2023-10-13 09:44:05 * Generated on: 2023-10-13 15:23:30
*/ */
#include "translateObjects.h" #include "translateObjects.h"

View File

@ -750,14 +750,18 @@ ReturnValue_t ObjectFactory::createCcsdsComponents(CcsdsComponentArgs& args) {
gpioChecker(args.gpioComIF.addGpios(gpioCookiePtmeIp), "PTME PAPB VCs"); gpioChecker(args.gpioComIF.addGpios(gpioCookiePtmeIp), "PTME PAPB VCs");
// Creating virtual channel interfaces // Creating virtual channel interfaces
VirtualChannelIF* vc0 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC0_PAPB_EMPTY, VirtualChannelIF* vc0 =
q7s::UIO_PTME, q7s::uiomapids::PTME_VC0); new PapbVcInterface(&args.gpioComIF, gpioIds::VC0_PAPB_EMPTY, q7s::UIO_PTME,
VirtualChannelIF* vc1 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC1_PAPB_EMPTY, q7s::uiomapids::PTME_VC0, config::MAX_SPACEPACKET_TC_SIZE);
q7s::UIO_PTME, q7s::uiomapids::PTME_VC1); VirtualChannelIF* vc1 =
VirtualChannelIF* vc2 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC2_PAPB_EMPTY, new PapbVcInterface(&args.gpioComIF, gpioIds::VC1_PAPB_EMPTY, q7s::UIO_PTME,
q7s::UIO_PTME, q7s::uiomapids::PTME_VC2); q7s::uiomapids::PTME_VC1, config::MAX_SPACEPACKET_TC_SIZE);
VirtualChannelIF* vc3 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC3_PAPB_EMPTY, VirtualChannelIF* vc2 =
q7s::UIO_PTME, q7s::uiomapids::PTME_VC3); new PapbVcInterface(&args.gpioComIF, gpioIds::VC2_PAPB_EMPTY, q7s::UIO_PTME,
q7s::uiomapids::PTME_VC2, config::MAX_SPACEPACKET_TC_SIZE);
VirtualChannelIF* vc3 =
new PapbVcInterface(&args.gpioComIF, gpioIds::VC3_PAPB_EMPTY, q7s::UIO_PTME,
q7s::uiomapids::PTME_VC3, config::MAX_SPACEPACKET_TC_SIZE);
// Creating ptme object and adding virtual channel interfaces // Creating ptme object and adding virtual channel interfaces
Ptme* ptme = new Ptme(objects::PTME); Ptme* ptme = new Ptme(objects::PTME);
ptme->addVcInterface(ccsds::VC0, vc0); ptme->addVcInterface(ccsds::VC0, vc0);

View File

@ -35,6 +35,8 @@ static constexpr uint32_t STR_IMG_HELPER_QUEUE_SIZE = 50;
static constexpr uint8_t LIVE_TM = 0; static constexpr uint8_t LIVE_TM = 0;
static constexpr size_t MAX_SPACEPACKET_TC_SIZE = 2048;
/* Limits for filename and path checks */ /* Limits for filename and path checks */
static constexpr uint32_t MAX_PATH_SIZE = 200; static constexpr uint32_t MAX_PATH_SIZE = 200;
static constexpr uint32_t MAX_FILENAME_SIZE = 100; static constexpr uint32_t MAX_FILENAME_SIZE = 100;
@ -48,7 +50,7 @@ static constexpr uint32_t LEGACY_SA_DEPL_CHANNEL_ALTERNATION_INTERVAL_SECS = 5;
// Maximum allowed burn time allowed by the software. // Maximum allowed burn time allowed by the software.
static constexpr uint32_t SA_DEPL_MAX_BURN_TIME = 180; static constexpr uint32_t SA_DEPL_MAX_BURN_TIME = 180;
static constexpr size_t CFDP_MAX_FILE_SEGMENT_LEN = 300; static constexpr size_t CFDP_MAX_FILE_SEGMENT_LEN = 990;
static constexpr uint32_t CCSDS_HANDLER_QUEUE_SIZE = 50; static constexpr uint32_t CCSDS_HANDLER_QUEUE_SIZE = 50;
static constexpr uint8_t NUMBER_OF_VIRTUAL_CHANNELS = 4; static constexpr uint8_t NUMBER_OF_VIRTUAL_CHANNELS = 4;
@ -61,9 +63,9 @@ static constexpr uint32_t HK_STORE_QUEUE_SIZE = 300;
static constexpr uint32_t CFDP_STORE_QUEUE_SIZE = 300; static constexpr uint32_t CFDP_STORE_QUEUE_SIZE = 300;
static constexpr uint32_t LIVE_CHANNEL_NORMAL_QUEUE_SIZE = 250; static constexpr uint32_t LIVE_CHANNEL_NORMAL_QUEUE_SIZE = 250;
static constexpr uint32_t LIVE_CHANNEL_CFDP_QUEUE_SIZE = 250; static constexpr uint32_t LIVE_CHANNEL_CFDP_QUEUE_SIZE = 400;
static constexpr uint32_t CFDP_MAX_FSM_CALL_COUNT_SRC_HANDLER = 50; static constexpr uint32_t CFDP_MAX_FSM_CALL_COUNT_SRC_HANDLER = 60;
static constexpr uint32_t CFDP_MAX_FSM_CALL_COUNT_DEST_HANDLER = 300; static constexpr uint32_t CFDP_MAX_FSM_CALL_COUNT_DEST_HANDLER = 300;
static constexpr uint32_t CFDP_SHORT_DELAY_MS = 50; static constexpr uint32_t CFDP_SHORT_DELAY_MS = 50;
static constexpr uint32_t CFDP_REGULAR_DELAY_MS = 200; static constexpr uint32_t CFDP_REGULAR_DELAY_MS = 200;

View File

@ -521,5 +521,5 @@ Full ID (hex); Name; Description; Unique ID; Subsytem Name; File Path
0x6e01;PTM_BusyDumping;No description;1;PERSISTENT_TM_STORE;mission/tmtc/PersistentTmStore.h 0x6e01;PTM_BusyDumping;No description;1;PERSISTENT_TM_STORE;mission/tmtc/PersistentTmStore.h
0x6f00;TMS_IsBusy;No description;0;TM_SINK;mission/tmtc/DirectTmSinkIF.h 0x6f00;TMS_IsBusy;No description;0;TM_SINK;mission/tmtc/DirectTmSinkIF.h
0x6f01;TMS_PartiallyWritten;No description;1;TM_SINK;mission/tmtc/DirectTmSinkIF.h 0x6f01;TMS_PartiallyWritten;No description;1;TM_SINK;mission/tmtc/DirectTmSinkIF.h
0x6f02;TMS_IncompletePartialWrite;No description;2;TM_SINK;mission/tmtc/DirectTmSinkIF.h 0x6f02;TMS_NoWriteActive;No description;2;TM_SINK;mission/tmtc/DirectTmSinkIF.h
0x7000;VCS_ChannelDoesNotExist;No description;0;VIRTUAL_CHANNEL;mission/com/VirtualChannel.h 0x7000;VCS_ChannelDoesNotExist;No description;0;VIRTUAL_CHANNEL;mission/com/VirtualChannel.h

1 Full ID (hex) Name Description Unique ID Subsytem Name File Path
521 0x6e01 PTM_BusyDumping No description 1 PERSISTENT_TM_STORE mission/tmtc/PersistentTmStore.h
522 0x6f00 TMS_IsBusy No description 0 TM_SINK mission/tmtc/DirectTmSinkIF.h
523 0x6f01 TMS_PartiallyWritten No description 1 TM_SINK mission/tmtc/DirectTmSinkIF.h
524 0x6f02 TMS_IncompletePartialWrite TMS_NoWriteActive No description 2 TM_SINK mission/tmtc/DirectTmSinkIF.h
525 0x7000 VCS_ChannelDoesNotExist No description 0 VIRTUAL_CHANNEL mission/com/VirtualChannel.h

View File

@ -616,6 +616,6 @@ Full ID (hex); Name; Description; Unique ID; Subsytem Name; File Path
0x6e01;PTM_BusyDumping;No description;1;PERSISTENT_TM_STORE;mission/tmtc/PersistentTmStore.h 0x6e01;PTM_BusyDumping;No description;1;PERSISTENT_TM_STORE;mission/tmtc/PersistentTmStore.h
0x6f00;TMS_IsBusy;No description;0;TM_SINK;mission/tmtc/DirectTmSinkIF.h 0x6f00;TMS_IsBusy;No description;0;TM_SINK;mission/tmtc/DirectTmSinkIF.h
0x6f01;TMS_PartiallyWritten;No description;1;TM_SINK;mission/tmtc/DirectTmSinkIF.h 0x6f01;TMS_PartiallyWritten;No description;1;TM_SINK;mission/tmtc/DirectTmSinkIF.h
0x6f02;TMS_IncompletePartialWrite;No description;2;TM_SINK;mission/tmtc/DirectTmSinkIF.h 0x6f02;TMS_NoWriteActive;No description;2;TM_SINK;mission/tmtc/DirectTmSinkIF.h
0x7000;VCS_ChannelDoesNotExist;No description;0;VIRTUAL_CHANNEL;mission/com/VirtualChannel.h 0x7000;VCS_ChannelDoesNotExist;No description;0;VIRTUAL_CHANNEL;mission/com/VirtualChannel.h
0x7200;SCBU_KeyNotFound;No description;0;SCRATCH_BUFFER;bsp_q7s/memory/scratchApi.h 0x7200;SCBU_KeyNotFound;No description;0;SCRATCH_BUFFER;bsp_q7s/memory/scratchApi.h

1 Full ID (hex) Name Description Unique ID Subsytem Name File Path
616 0x6e01 PTM_BusyDumping No description 1 PERSISTENT_TM_STORE mission/tmtc/PersistentTmStore.h
617 0x6f00 TMS_IsBusy No description 0 TM_SINK mission/tmtc/DirectTmSinkIF.h
618 0x6f01 TMS_PartiallyWritten No description 1 TM_SINK mission/tmtc/DirectTmSinkIF.h
619 0x6f02 TMS_IncompletePartialWrite TMS_NoWriteActive No description 2 TM_SINK mission/tmtc/DirectTmSinkIF.h
620 0x7000 VCS_ChannelDoesNotExist No description 0 VIRTUAL_CHANNEL mission/com/VirtualChannel.h
621 0x7200 SCBU_KeyNotFound No description 0 SCRATCH_BUFFER bsp_q7s/memory/scratchApi.h

View File

@ -1,7 +1,7 @@
/** /**
* @brief Auto-generated event translation file. Contains 314 translations. * @brief Auto-generated event translation file. Contains 314 translations.
* @details * @details
* Generated on: 2023-10-13 09:44:05 * Generated on: 2023-10-13 15:23:30
*/ */
#include "translateEvents.h" #include "translateEvents.h"

View File

@ -2,7 +2,7 @@
* @brief Auto-generated object translation file. * @brief Auto-generated object translation file.
* @details * @details
* Contains 178 translations. * Contains 178 translations.
* Generated on: 2023-10-13 09:44:05 * Generated on: 2023-10-13 15:23:30
*/ */
#include "translateObjects.h" #include "translateObjects.h"

View File

@ -1,7 +1,7 @@
/** /**
* @brief Auto-generated event translation file. Contains 314 translations. * @brief Auto-generated event translation file. Contains 314 translations.
* @details * @details
* Generated on: 2023-10-13 09:44:05 * Generated on: 2023-10-13 15:23:30
*/ */
#include "translateEvents.h" #include "translateEvents.h"

View File

@ -2,7 +2,7 @@
* @brief Auto-generated object translation file. * @brief Auto-generated object translation file.
* @details * @details
* Contains 178 translations. * Contains 178 translations.
* Generated on: 2023-10-13 09:44:05 * Generated on: 2023-10-13 15:23:30
*/ */
#include "translateObjects.h" #include "translateObjects.h"

View File

@ -8,8 +8,12 @@
#include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/serviceinterface/ServiceInterface.h"
PapbVcInterface::PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbEmptyId, PapbVcInterface::PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbEmptyId,
std::string uioFile, int mapNum) std::string uioFile, int mapNum, size_t maxPacketSize)
: gpioComIF(gpioComIF), papbEmptyId(papbEmptyId), uioFile(std::move(uioFile)), mapNum(mapNum) {} : gpioComIF(gpioComIF),
papbEmptyId(papbEmptyId),
packetBuf(maxPacketSize),
uioFile(std::move(uioFile)),
mapNum(mapNum) {}
PapbVcInterface::~PapbVcInterface() {} PapbVcInterface::~PapbVcInterface() {}
@ -28,10 +32,18 @@ ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size, size_t& w
if (size < 4) { if (size < 4) {
return returnvalue::FAILED; return returnvalue::FAILED;
} }
// The user must call finishWrite before starting a new packet transfer. // The user must call advance until completion before starting a new packet transfer.
if (partialWriteActive) { if (writeActiveStatus) {
return INCOMPLETE_PARTIAL_WRITE; return IS_BUSY;
} }
if (size > packetBuf.capacity()) {
sif::error << "PapbVcInterface: Packet with size " << size << " larger than maximum configured"
<< " byte size " << packetBuf.capacity() << std::endl;
return returnvalue::FAILED;
}
std::memcpy(packetBuf.data(), data, size);
currentPacketSize = size;
currentPacketIndex = 0;
if (pollReadyForPacket()) { if (pollReadyForPacket()) {
startPacketTransfer(ByteWidthCfg::ONE); startPacketTransfer(ByteWidthCfg::ONE);
} else { } else {
@ -41,15 +53,14 @@ ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size, size_t& w
abortPacketTransfer(); abortPacketTransfer();
return returnvalue::FAILED; return returnvalue::FAILED;
} }
return finishWriteInternal(data, 0, size, writtenSize, false); return advanceWrite(writtenSize);
} }
void PapbVcInterface::startPacketTransfer(ByteWidthCfg initWidth) { void PapbVcInterface::startPacketTransfer(ByteWidthCfg initWidth) {
*vcBaseReg = CONFIG_DATA_INPUT | initWidth; *vcBaseReg = CONFIG_DATA_INPUT | initWidth;
writeActiveStatus = true;
} }
void PapbVcInterface::completePacketTransfer() { *vcBaseReg = CONFIG_END; }
bool PapbVcInterface::pollReadyForPacket() const { bool PapbVcInterface::pollReadyForPacket() const {
// Check if PAPB interface is ready to receive data. Use the configuration register for this. // Check if PAPB interface is ready to receive data. Use the configuration register for this.
// Bit 5, see PTME ptme_001_01-0-7-r2 Table 31. // Bit 5, see PTME ptme_001_01-0-7-r2 Table 31.
@ -57,34 +68,23 @@ bool PapbVcInterface::pollReadyForPacket() const {
return (reg >> 6) & 0b1; return (reg >> 6) & 0b1;
} }
ReturnValue_t PapbVcInterface::finishWrite(const uint8_t* data, size_t start, ReturnValue_t PapbVcInterface::advanceWrite(size_t& writtenSize) {
size_t remainingSize) { if (!writeActiveStatus) {
if (not pollReadyForPacket()) { return NO_WRITE_ACTIVE;
return returnvalue::FAILED;
} }
size_t dummy = 0; if (not pollReadyForPacket()) {
return finishWriteInternal(data, start, remainingSize, dummy, true); return IS_BUSY;
} }
for (size_t idx = currentPacketIndex; idx < currentPacketSize; idx++) {
ReturnValue_t PapbVcInterface::finishWriteInternal(const uint8_t* data, size_t start,
size_t remainingSize, size_t& writtenSize,
bool abortOnPartialWrite) {
for (size_t idx = 0; idx < remainingSize; idx++) {
if (not pollReadyForOctet(MAX_BUSY_POLLS)) { if (not pollReadyForOctet(MAX_BUSY_POLLS)) {
if (not pollReadyForPacket()) { if (not pollReadyForPacket()) {
writtenSize = start + idx;
partialWriteActive = true;
if (abortOnPartialWrite) {
abortPacketTransfer();
partialWriteActive = false;
return returnvalue::FAILED;
}
return PARTIALLY_WRITTEN; return PARTIALLY_WRITTEN;
} }
abortPacketTransfer(); abortPacketTransfer();
return returnvalue::FAILED; return returnvalue::FAILED;
} }
*(vcBaseReg + DATA_REG_OFFSET) = static_cast<uint32_t>(data[start + idx]); *(vcBaseReg + DATA_REG_OFFSET) = static_cast<uint32_t>(packetBuf[currentPacketIndex]);
writtenSize++;
} }
if (not pollReadyForOctet(MAX_BUSY_POLLS)) { if (not pollReadyForOctet(MAX_BUSY_POLLS)) {
abortPacketTransfer(); abortPacketTransfer();
@ -94,6 +94,8 @@ ReturnValue_t PapbVcInterface::finishWriteInternal(const uint8_t* data, size_t s
return returnvalue::OK; return returnvalue::OK;
} }
bool PapbVcInterface::writeActive() const { return writeActiveStatus; }
bool PapbVcInterface::isVcInterfaceBufferEmpty() { bool PapbVcInterface::isVcInterfaceBufferEmpty() {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
gpio::Levels papbEmptyState = gpio::Levels::HIGH; gpio::Levels papbEmptyState = gpio::Levels::HIGH;
@ -130,4 +132,16 @@ inline bool PapbVcInterface::pollReadyForOctet(uint32_t maxCycles) const {
return false; return false;
} }
void PapbVcInterface::abortPacketTransfer() { *vcBaseReg = CONFIG_ABORT; } void PapbVcInterface::abortPacketTransfer() {
*vcBaseReg = CONFIG_ABORT;
writeActiveStatus = false;
currentPacketIndex = 0;
currentPacketSize = 0;
}
void PapbVcInterface::completePacketTransfer() {
*vcBaseReg = CONFIG_END;
writeActiveStatus = false;
currentPacketIndex = 0;
currentPacketSize = 0;
}

View File

@ -6,6 +6,7 @@
#include <linux/ipcore/VirtualChannelIF.h> #include <linux/ipcore/VirtualChannelIF.h>
#include <atomic> #include <atomic>
#include <vector>
#include "OBSWConfig.h" #include "OBSWConfig.h"
#include "fsfw/returnvalues/returnvalue.h" #include "fsfw/returnvalues/returnvalue.h"
@ -30,24 +31,22 @@ class PapbVcInterface : public VirtualChannelIF {
* @param uioFile UIO file providing access to the PAPB bus * @param uioFile UIO file providing access to the PAPB bus
* @param mapNum Map number of UIO map associated with this virtual channel * @param mapNum Map number of UIO map associated with this virtual channel
*/ */
PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbEmptyId, std::string uioFile, int mapNum); PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbEmptyId, std::string uioFile, int mapNum,
size_t maxPacketSize);
virtual ~PapbVcInterface(); virtual ~PapbVcInterface();
// See interface function documentation for docs on these functions.
bool isBusy() const override; bool isBusy() const override;
/**
*
* @param data
* @param size
* @return returnvalue::OK on successfull write, PAPB_BUSY if PAPB is busy.
*/
ReturnValue_t write(const uint8_t* data, size_t size, size_t& writtenSize) override; ReturnValue_t write(const uint8_t* data, size_t size, size_t& writtenSize) override;
ReturnValue_t finishWrite(const uint8_t* data, size_t start, size_t remainingSize) override; ReturnValue_t advanceWrite(size_t& remainingSize) override;
ReturnValue_t finishWriteInternal(const uint8_t* data, size_t start, size_t remainingSize,
size_t& writtenSize, bool abortOnPartialWrite);
void cancelTransfer() override; void cancelTransfer() override;
bool writeActive() const override;
ReturnValue_t initialize() override; ReturnValue_t initialize() override;
private: private:
@ -90,12 +89,14 @@ class PapbVcInterface : public VirtualChannelIF {
/** High when external buffer memory of virtual channel is empty */ /** High when external buffer memory of virtual channel is empty */
gpioId_t papbEmptyId = gpio::NO_GPIO; gpioId_t papbEmptyId = gpio::NO_GPIO;
std::vector<uint8_t> packetBuf;
std::string uioFile; std::string uioFile;
int mapNum = 0; int mapNum = 0;
bool partialWriteActive = false; bool writeActiveStatus = false;
size_t currentPacketIndex = 0;
size_t currentPacketSize = 0;
mutable struct timespec nextDelay = {.tv_sec = 0, .tv_nsec = 0}; mutable struct timespec nextDelay = {.tv_sec = 0, .tv_nsec = 0};
const struct timespec BETWEEN_POLL_DELAY = {.tv_sec = 0, .tv_nsec = 10}; const struct timespec BETWEEN_POLL_DELAY = {.tv_sec = 0, .tv_nsec = 10};
mutable struct timespec remDelay;
volatile uint32_t* vcBaseReg = nullptr; volatile uint32_t* vcBaseReg = nullptr;

View File

@ -16,7 +16,7 @@ using namespace returnvalue;
using namespace cfdp; using namespace cfdp;
CfdpHandler::CfdpHandler(const FsfwHandlerParams& fsfwHandlerParams, const CfdpHandlerCfg& cfdpCfg, CfdpHandler::CfdpHandler(const FsfwHandlerParams& fsfwHandlerParams, const CfdpHandlerCfg& cfdpCfg,
const std::atomic_bool& throttleSignal) const std::atomic_bool& throttleSignal)
: SystemObject(fsfwHandlerParams.objectId), : SystemObject(fsfwHandlerParams.objectId),
pduQueue(fsfwHandlerParams.tmtcQueue), pduQueue(fsfwHandlerParams.tmtcQueue),
cfdpRequestQueue(fsfwHandlerParams.cfdpQueue), cfdpRequestQueue(fsfwHandlerParams.cfdpQueue),
@ -60,7 +60,7 @@ ReturnValue_t CfdpHandler::initialize() {
result = handleCfdpMessages(); result = handleCfdpMessages();
if (result != OK) { if (result != OK) {
} }
uint32_t fsmCount = 0; uint32_t fsmCount = 1;
const DestHandler::FsmResult& destResult = destHandler.stateMachine(); const DestHandler::FsmResult& destResult = destHandler.stateMachine();
while (destResult.callStatus == CallStatus::CALL_AGAIN) { while (destResult.callStatus == CallStatus::CALL_AGAIN) {
if (fsmCount == config::CFDP_MAX_FSM_CALL_COUNT_DEST_HANDLER) { if (fsmCount == config::CFDP_MAX_FSM_CALL_COUNT_DEST_HANDLER) {
@ -70,16 +70,17 @@ ReturnValue_t CfdpHandler::initialize() {
destHandler.stateMachine(); destHandler.stateMachine();
fsmCount++; fsmCount++;
} }
fsmCount = 0; fsmCount = 1;
if (throttleSignal) { throttlePeriodOngoing = throttleSignal;
throttlePeriodOngoing = true;
}
// CFDP can be throttled by the slowest live TM handler to handle back pressure in a sensible // CFDP can be throttled by the slowest live TM handler to handle back pressure in a sensible
// way without requiring huge amounts of memory for large files. // way without requiring huge amounts of memory for large files.
if (!throttlePeriodOngoing) { if (!throttlePeriodOngoing) {
const SourceHandler::FsmResult& srcResult = srcHandler.stateMachine(); const SourceHandler::FsmResult& srcResult = srcHandler.stateMachine();
if (srcResult.packetsSent > 0) {
signals::CFDP_MSG_COUNTER.fetch_add(srcResult.packetsSent, std::memory_order_relaxed);
}
while (srcResult.callStatus == CallStatus::CALL_AGAIN) { while (srcResult.callStatus == CallStatus::CALL_AGAIN) {
// Limit number of messages. // Limit number of messages.
if (fsmCount == config::CFDP_MAX_FSM_CALL_COUNT_SRC_HANDLER) { if (fsmCount == config::CFDP_MAX_FSM_CALL_COUNT_SRC_HANDLER) {
@ -87,6 +88,9 @@ ReturnValue_t CfdpHandler::initialize() {
break; break;
} }
srcHandler.stateMachine(); srcHandler.stateMachine();
if (srcResult.packetsSent > 0) {
signals::CFDP_MSG_COUNTER.fetch_add(srcResult.packetsSent, std::memory_order_relaxed);
}
if (srcResult.result == cfdp::TM_STORE_FULL) { if (srcResult.result == cfdp::TM_STORE_FULL) {
sif::warning << "CFDP Source Handler: TM store is full" << std::endl; sif::warning << "CFDP Source Handler: TM store is full" << std::endl;
} else if (srcResult.result == cfdp::TARGET_MSG_QUEUE_FULL) { } else if (srcResult.result == cfdp::TARGET_MSG_QUEUE_FULL) {

View File

@ -63,7 +63,7 @@ struct CfdpHandlerCfg {
class CfdpHandler : public SystemObject, public ExecutableObjectIF, public AcceptsTelecommandsIF { class CfdpHandler : public SystemObject, public ExecutableObjectIF, public AcceptsTelecommandsIF {
public: public:
explicit CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg, explicit CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg,
const std::atomic_bool& throttleSignal); const std::atomic_bool& throttleSignal);
[[nodiscard]] const char* getName() const override; [[nodiscard]] const char* getName() const override;
[[nodiscard]] uint32_t getIdentifier() const override; [[nodiscard]] uint32_t getIdentifier() const override;

View File

@ -9,6 +9,7 @@
static constexpr bool DEBUG_TM_QUEUE_SPEED = false; static constexpr bool DEBUG_TM_QUEUE_SPEED = false;
std::atomic_bool signals::CFDP_CHANNEL_THROTTLE_SIGNAL = false; std::atomic_bool signals::CFDP_CHANNEL_THROTTLE_SIGNAL = false;
std::atomic_uint32_t signals::CFDP_MSG_COUNTER = 0;
LiveTmTask::LiveTmTask(object_id_t objectId, PusTmFunnel& pusFunnel, CfdpTmFunnel& cfdpFunnel, LiveTmTask::LiveTmTask(object_id_t objectId, PusTmFunnel& pusFunnel, CfdpTmFunnel& cfdpFunnel,
VirtualChannel& channel, const std::atomic_bool& ptmeLocked, VirtualChannel& channel, const std::atomic_bool& ptmeLocked,
@ -36,7 +37,8 @@ ReturnValue_t LiveTmTask::performOperation(uint8_t opCode) {
// TODO: Must read CFDP TM queue and regular TM queue and forward them. Handle regular queue // TODO: Must read CFDP TM queue and regular TM queue and forward them. Handle regular queue
// first. // first.
handledTm = false; handledTm = false;
if (!channel.isBusy()) { updateBusyFlag();
if (!channelIsBusy) {
result = handleRegularTmQueue(); result = handleRegularTmQueue();
if (result == MessageQueueIF::EMPTY) { if (result == MessageQueueIF::EMPTY) {
result = handleCfdpTmQueue(); result = handleCfdpTmQueue();
@ -51,17 +53,14 @@ ReturnValue_t LiveTmTask::performOperation(uint8_t opCode) {
consecutiveRegularCounter++; consecutiveRegularCounter++;
} }
} }
} else if (result != MessageQueueIF::EMPTY) {
sif::warning << "LiveTmTask: TM queue failure, returncode 0x" << std::hex << std::setw(4)
<< result << std::dec << std::endl;
} }
} }
if (channel.isBusy() and !throttlePeriodOngoing) {
// Throttle CFDP packet creator. It is by far the most relevant data creator, so throttling cfdpBackpressureHandling();
// it is the easiest way to handle back pressure for now in a sensible way.
throttleCfdp();
} else if(!channel.isBusy() and throttlePeriodOngoing) {
if(minimumPeriodThrottleCd.hasTimedOut()) {
releaseCfdp();
}
}
if (!handledTm) { if (!handledTm) {
if (tmFunnelCd.hasTimedOut()) { if (tmFunnelCd.hasTimedOut()) {
pusFunnel.performOperation(0); pusFunnel.performOperation(0);
@ -72,10 +71,10 @@ ReturnValue_t LiveTmTask::performOperation(uint8_t opCode) {
readCommandQueue(); readCommandQueue();
if (DEBUG_TM_QUEUE_SPEED) { if (DEBUG_TM_QUEUE_SPEED) {
if (consecutiveCfdpCounter > 0) { if (consecutiveCfdpCounter > 0) {
sif::debug << "Concecutive CFDP TM handled: " << consecutiveCfdpCounter << std::endl; sif::debug << "Consecutive CFDP TM handled: " << consecutiveCfdpCounter << std::endl;
} }
if (consecutiveRegularCounter > 0) { if (consecutiveRegularCounter > 0) {
sif::debug << "Concecutive regular TM handled: " << consecutiveRegularCounter sif::debug << "Consecutive regular TM handled: " << consecutiveRegularCounter
<< std::endl; << std::endl;
} }
consecutiveRegularCounter = 0; consecutiveRegularCounter = 0;
@ -141,16 +140,24 @@ void LiveTmTask::readCommandQueue(void) {
} }
} }
ReturnValue_t LiveTmTask::handleRegularTmQueue() { return handleGenericTmQueue(*regularTmQueue); } ReturnValue_t LiveTmTask::handleRegularTmQueue() {
return handleGenericTmQueue(*regularTmQueue, false);
}
ReturnValue_t LiveTmTask::handleCfdpTmQueue() { return handleGenericTmQueue(*cfdpTmQueue); } ReturnValue_t LiveTmTask::handleCfdpTmQueue() { return handleGenericTmQueue(*cfdpTmQueue, true); }
ReturnValue_t LiveTmTask::handleGenericTmQueue(MessageQueueIF& queue) { ReturnValue_t LiveTmTask::handleGenericTmQueue(MessageQueueIF& queue, bool isCfdp) {
TmTcMessage message; TmTcMessage message;
ReturnValue_t result = queue.receiveMessage(&message); ReturnValue_t result = queue.receiveMessage(&message);
if (result == MessageQueueIF::EMPTY) { if (result == MessageQueueIF::EMPTY) {
return result; return result;
} }
if (isCfdp and signals::CFDP_MSG_COUNTER > 0) {
signals::CFDP_MSG_COUNTER--;
}
if (DEBUG_CFDP_TO_LIVE_TM_TASK and signals::CFDP_MSG_COUNTER > 0) {
sif::debug << "LiveTmTask: CFDP message counter: " << signals::CFDP_MSG_COUNTER << std::endl;
}
store_address_t storeId = message.getStorageId(); store_address_t storeId = message.getStorageId();
const uint8_t* data = nullptr; const uint8_t* data = nullptr;
size_t size = 0; size_t size = 0;
@ -163,17 +170,14 @@ ReturnValue_t LiveTmTask::handleGenericTmQueue(MessageQueueIF& queue) {
} }
if (!ptmeLocked) { if (!ptmeLocked) {
size_t partiallyWrittenSize = 0; size_t writtenSize = 0;
result = channel.write(data, size, partiallyWrittenSize); result = channel.write(data, size, writtenSize);
if (result == DirectTmSinkIF::PARTIALLY_WRITTEN) { if (result == DirectTmSinkIF::PARTIALLY_WRITTEN) {
// Already throttle CFDP. result = channel.handleWriteCompletionSynchronously(writtenSize, 200);
throttleCfdp();
result = channel.handleLastWriteSynchronously(data, size, partiallyWrittenSize, 200);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
// TODO: Event? Might lead to dangerous spam though.. // TODO: Event? Might lead to dangerous spam though..
sif::warning sif::warning << "LiveTmTask: Synchronous write of last segment failed with code 0x"
<< "LiveTmTask: Synchronous write of last segment failed with code 0x" << std::setw(4) << std::hex << result << std::dec << std::endl;
<< std::setw(4) << std::hex << result << std::dec << std::endl;
} }
} }
} }
@ -185,13 +189,38 @@ ReturnValue_t LiveTmTask::handleGenericTmQueue(MessageQueueIF& queue) {
void LiveTmTask::throttleCfdp() { void LiveTmTask::throttleCfdp() {
throttlePeriodOngoing = true; throttlePeriodOngoing = true;
minimumPeriodThrottleCd.resetTimer();
signals::CFDP_CHANNEL_THROTTLE_SIGNAL = true; signals::CFDP_CHANNEL_THROTTLE_SIGNAL = true;
if (DEBUG_CFDP_TO_LIVE_TM_TASK) {
sif::debug << "Throttling CFDP" << std::endl;
}
} }
void LiveTmTask::releaseCfdp() { void LiveTmTask::releaseCfdp() {
throttlePeriodOngoing = false; throttlePeriodOngoing = false;
signals::CFDP_CHANNEL_THROTTLE_SIGNAL = false; signals::CFDP_CHANNEL_THROTTLE_SIGNAL = false;
if (DEBUG_CFDP_TO_LIVE_TM_TASK) {
sif::debug << "Releasing CFDP" << std::endl;
}
}
void LiveTmTask::updateBusyFlag() {
// We cache this as a member, because the busy bit can toggle very quickly..
channelIsBusy = channel.isBusy();
}
void LiveTmTask::cfdpBackpressureHandling() {
if (channelIsBusy and !throttlePeriodOngoing) {
// Throttle CFDP packet creator. It is by far the most relevant data creator, so throttling
// it is the easiest way to handle back pressure for now in a sensible way.
if (signals::CFDP_MSG_COUNTER >= (config::LIVE_CHANNEL_CFDP_QUEUE_SIZE / 2)) {
throttleCfdp();
}
} else if (!channelIsBusy and throttlePeriodOngoing) {
// Half full/empty flow control: Release the CFDP is the queue is empty enough.
if (signals::CFDP_MSG_COUNTER <= (config::LIVE_CHANNEL_CFDP_QUEUE_SIZE / 4)) {
releaseCfdp();
}
}
} }
ModeTreeChildIF& LiveTmTask::getModeTreeChildIF() { return *this; } ModeTreeChildIF& LiveTmTask::getModeTreeChildIF() { return *this; }

View File

@ -11,6 +11,12 @@
#include <mission/tmtc/CfdpTmFunnel.h> #include <mission/tmtc/CfdpTmFunnel.h>
#include <mission/tmtc/PusTmFunnel.h> #include <mission/tmtc/PusTmFunnel.h>
#include <cstdint>
#include "eive/definitions.h"
static constexpr bool DEBUG_CFDP_TO_LIVE_TM_TASK = false;
class LiveTmTask : public SystemObject, class LiveTmTask : public SystemObject,
public HasModesIF, public HasModesIF,
public ExecutableObjectIF, public ExecutableObjectIF,
@ -39,21 +45,21 @@ class LiveTmTask : public SystemObject,
CfdpTmFunnel& cfdpFunnel; CfdpTmFunnel& cfdpFunnel;
VirtualChannel& channel; VirtualChannel& channel;
const std::atomic_bool& ptmeLocked; const std::atomic_bool& ptmeLocked;
// This countdown ensures that the CFDP is always throttled with a minimum period. Only after
// this period, the CFDP can be released if the channel is not busy.
Countdown minimumPeriodThrottleCd = Countdown(40);
bool throttlePeriodOngoing = false; bool throttlePeriodOngoing = false;
bool channelIsBusy = false;
void readCommandQueue(void); void readCommandQueue(void);
ReturnValue_t handleRegularTmQueue(); ReturnValue_t handleRegularTmQueue();
ReturnValue_t handleCfdpTmQueue(); ReturnValue_t handleCfdpTmQueue();
ReturnValue_t handleGenericTmQueue(MessageQueueIF& queue); ReturnValue_t handleGenericTmQueue(MessageQueueIF& queue, bool isCfdp);
MessageQueueId_t getCommandQueue() const override; MessageQueueId_t getCommandQueue() const override;
void getMode(Mode_t* mode, Submode_t* submode) override; void getMode(Mode_t* mode, Submode_t* submode) override;
void cfdpBackpressureHandling();
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t* msToReachTheMode) override; uint32_t* msToReachTheMode) override;
@ -62,6 +68,7 @@ class LiveTmTask : public SystemObject,
void announceMode(bool recursive) override; void announceMode(bool recursive) override;
void throttleCfdp(); void throttleCfdp();
void releaseCfdp(); void releaseCfdp();
void updateBusyFlag();
object_id_t getObjectId() const override; object_id_t getObjectId() const override;
const HasHealthIF* getOptHealthIF() const override; const HasHealthIF* getOptHealthIF() const override;

View File

@ -138,11 +138,10 @@ ReturnValue_t TmStoreTaskBase::performDump(PersistentTmStoreWithTmQueue& store,
return result; return result;
} }
dumpedLen = tmReader.getFullPacketLen(); dumpedLen = tmReader.getFullPacketLen();
size_t partiallyWrittenSize = 0; size_t writtenSize = 0;
result = channel.write(tmReader.getFullData(), dumpedLen, partiallyWrittenSize); result = channel.write(tmReader.getFullData(), dumpedLen, writtenSize);
if (result == VirtualChannelIF::PARTIALLY_WRITTEN) { if (result == VirtualChannelIF::PARTIALLY_WRITTEN) {
result = channel.handleLastWriteSynchronously(tmReader.getFullData(), partiallyWrittenSize, result = channel.handleWriteCompletionSynchronously(writtenSize, 200);
dumpedLen - partiallyWrittenSize, 200);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
// TODO: Event? Might lead to dangerous spam though.. // TODO: Event? Might lead to dangerous spam though..
sif::warning << "PersistentTmStore: Synchronous write of last segment failed with code 0x" sif::warning << "PersistentTmStore: Synchronous write of last segment failed with code 0x"

View File

@ -21,11 +21,18 @@ ReturnValue_t VirtualChannel::write(const uint8_t* data, size_t size, size_t& wr
uint8_t VirtualChannel::getVcid() const { return vcId; } uint8_t VirtualChannel::getVcid() const { return vcId; }
ReturnValue_t VirtualChannel::finishWrite(const uint8_t* data, size_t start, size_t remainingSize) { ReturnValue_t VirtualChannel::advanceWrite(size_t& writtenSize) {
if (!ptme.containsVc(vcId)) { if (!ptme.containsVc(vcId)) {
return CHANNEL_DOES_NOT_EXIST; return CHANNEL_DOES_NOT_EXIST;
} }
return ptme.getVirtChannel(vcId)->finishWrite(data, start, remainingSize); return ptme.getVirtChannel(vcId)->advanceWrite(writtenSize);
}
bool VirtualChannel::writeActive() const {
if (!ptme.containsVc(vcId)) {
return CHANNEL_DOES_NOT_EXIST;
}
return ptme.getVirtChannel(vcId)->writeActive();
} }
const char* VirtualChannel::getName() const { return vcName.c_str(); } const char* VirtualChannel::getName() const { return vcName.c_str(); }
@ -46,20 +53,26 @@ void VirtualChannel::cancelTransfer() {
bool VirtualChannel::isTxOn() const { return txOn; } bool VirtualChannel::isTxOn() const { return txOn; }
ReturnValue_t VirtualChannel::handleLastWriteSynchronously(const uint8_t* data, size_t start, ReturnValue_t VirtualChannel::handleWriteCompletionSynchronously(size_t& writtenSize,
size_t remLen, unsigned maxDelayMs) { unsigned maxCompletionTimeMs) {
unsigned delayMs = 0; unsigned delayMs = 0;
while (true) { while (true) {
if (isBusy()) { if (isBusy()) {
if (delayMs >= maxDelayMs) { if (delayMs >= maxCompletionTimeMs) {
break; break;
} }
TaskFactory::delayTask(10); TaskFactory::delayTask(10);
delayMs += 10; delayMs += 10;
continue; continue;
} }
sif::debug << "last write after" << delayMs << std::endl; ReturnValue_t result = advanceWrite(writtenSize);
return finishWrite(data, start, remLen); if (result == returnvalue::OK) {
// Transfer complete
return result;
} else if (result != PARTIALLY_WRITTEN) {
// Some error where we can not or should not continue the transfer.
return result;
}
} }
return returnvalue::FAILED; return returnvalue::FAILED;
} }

View File

@ -32,9 +32,10 @@ class VirtualChannel : public SystemObject, public VirtualChannelIF {
ReturnValue_t sendNextTm(const uint8_t* data, size_t size, size_t& writtenSize); ReturnValue_t sendNextTm(const uint8_t* data, size_t size, size_t& writtenSize);
bool isBusy() const override; bool isBusy() const override;
ReturnValue_t write(const uint8_t* data, size_t size, size_t& writtenSize) override; ReturnValue_t write(const uint8_t* data, size_t size, size_t& writtenSize) override;
ReturnValue_t finishWrite(const uint8_t* data, size_t start, size_t remainingSize) override; ReturnValue_t advanceWrite(size_t& writtenSize) override;
ReturnValue_t handleLastWriteSynchronously(const uint8_t* data, size_t start, size_t remLen, ReturnValue_t handleWriteCompletionSynchronously(size_t& writtenSize,
unsigned maxDelayMs); unsigned maxCompletionTimeMs);
bool writeActive() const override;
void cancelTransfer() override; void cancelTransfer() override;
uint8_t getVcid() const; uint8_t getVcid() const;
bool isTxOn() const; bool isTxOn() const;

View File

@ -37,11 +37,11 @@ ReturnValue_t VirtualChannelWithQueue::handleNextTm(bool performWriteOp) {
} }
// TODO: Hnadle partial write handling // TODO: Hnadle partial write handling
size_t partiallyWrittenSize = 0; size_t writtenSize = 0;
if (performWriteOp) { if (performWriteOp) {
result = write(data, size, partiallyWrittenSize); result = write(data, size, writtenSize);
if (result == PARTIALLY_WRITTEN) { if (result == PARTIALLY_WRITTEN) {
result = handleLastWriteSynchronously(data, size, partiallyWrittenSize, 200); result = handleWriteCompletionSynchronously(writtenSize, 200);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
// TODO: Event? Might lead to dangerous spam though.. // TODO: Event? Might lead to dangerous spam though..
sif::warning sif::warning

View File

@ -15,6 +15,7 @@ namespace signals {
extern std::atomic_bool CFDP_CHANNEL_THROTTLE_SIGNAL; extern std::atomic_bool CFDP_CHANNEL_THROTTLE_SIGNAL;
extern std::atomic_uint16_t I2C_FATAL_ERRORS; extern std::atomic_uint16_t I2C_FATAL_ERRORS;
extern std::atomic_uint32_t CFDP_MSG_COUNTER;
} // namespace signals } // namespace signals

View File

@ -14,22 +14,44 @@ class DirectTmSinkIF {
static constexpr ReturnValue_t IS_BUSY = returnvalue::makeCode(CLASS_ID, 0); static constexpr ReturnValue_t IS_BUSY = returnvalue::makeCode(CLASS_ID, 0);
static constexpr ReturnValue_t PARTIALLY_WRITTEN = returnvalue::makeCode(CLASS_ID, 1); static constexpr ReturnValue_t PARTIALLY_WRITTEN = returnvalue::makeCode(CLASS_ID, 1);
static constexpr ReturnValue_t INCOMPLETE_PARTIAL_WRITE = returnvalue::makeCode(CLASS_ID, 2); static constexpr ReturnValue_t NO_WRITE_ACTIVE = returnvalue::makeCode(CLASS_ID, 2);
/** /**
* @brief Implements the functionality to write to a TM sink directly * @brief Implements the functionality to write to a TM sink directly.
*
* The write might not be completed immediately! If PARTIALLY_WRITTEN is returned, the user
* should poll the ready for packet status bit and call @advanceWrite continuously until
* the transfer is completed.
* *
* @param data Pointer to buffer holding the data to write * @param data Pointer to buffer holding the data to write
* @param size Number of bytes to write * @param size Number of bytes to write
* @return returnvalue::OK on success, returnvalue::FAILED on failure, IS_BUSY * @param writtenSize Size written during write call.
* if the TM sink is busy, PARTIALLY_WRITTEN if only a portion of the bytes could be * @return returnvalue::OK on full write success, IS_BUSY if a previous write transfer has not
* written. * been completed yet or the PAPB interface is not ready for a packet, PARTIALLY_WRITTEN
* if some bytes were written, but the transfer has not been completed yet.
*/ */
virtual ReturnValue_t write(const uint8_t* data, size_t size, size_t& writtenSize) = 0; virtual ReturnValue_t write(const uint8_t* data, size_t size, size_t& writtenSize) = 0;
virtual ReturnValue_t finishWrite(const uint8_t* data, size_t start, size_t remainingSize) = 0; /**
* Advances a active file transfer.
* @param writtenSize
* @return returnvalue::OK if the packet write process is complete, PARTIALLY_WRITTEN if
* some bytes were written but the transfer is not complete yet.
* NO_WRITE_ACTIVE if this is called without a valid previous write call.
*/
virtual ReturnValue_t advanceWrite(size_t& writtenSize) = 0;
/**
* Is busy, so no write operation can not be started and write advancement
* is not possible.
* @return
*/
virtual bool isBusy() const = 0; virtual bool isBusy() const = 0;
/**
* The PAPB interface is currently busy writing a packet and a new packet can not be written yet.
* @return
*/
virtual bool writeActive() const = 0;
}; };
#endif /* MISSION_TMTC_DIRECTTMSINKIF_H_ */ #endif /* MISSION_TMTC_DIRECTTMSINKIF_H_ */

2
tmtc

@ -1 +1 @@
Subproject commit 10e163be752a6a259b6d3dabea825acc9c9725f8 Subproject commit 60f7ae5453b387ee5ebcf6a338c34284004dbce7