Merge pull request 'PTME rework reset handling' (#542) from ptme_rework_reset_handling into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good

Reviewed-on: #542
This commit is contained in:
Robin Müller 2023-03-31 17:26:52 +02:00
commit 9d42bb87d9
37 changed files with 773 additions and 380 deletions

View File

@ -19,11 +19,19 @@ will consitute of a breaking change warranting a new major release:
## Changed
- SCEX filename updates. Also use T as the file ID / date separator between date and time.
- COM TM store and dump handling: Introduce modes for all 4 TM VC/store tasks. The OFF mode can be
used to disable ongoing dumps or to prevent writes to the PTME VC. This allows cleaner reset
handling of the PTME. All 4 VC/store tasks were attached to the COM mode tree and are commanded
as part of the COM sequence as well to ensure consistent state with the CCSDS IP core handler.
- Added `PTME_LOCKED` boolean lock which is used to lock the PTME so it is not used by the VC tasks
anymore. This lock will be controlled by the CCSDS IP core handler and is locked when the PTME
needs to be reset. Examples for this are datarate changes.
## Fixed
- Bugfix for side lane transitions of the dual lane assemblies, which only worked when the
assembly was directly commanded
- Syrlinks Handler: Bugfix so transition command is only sent once.
## Added

View File

@ -486,7 +486,8 @@ endif()
target_link_libraries(${LIB_EIVE_MISSION} PUBLIC ${LIB_FSFW_NAME}
${LIB_OS_NAME})
target_link_libraries(${LIB_DUMMIES} PUBLIC ${LIB_FSFW_NAME} ${LIB_JSON_NAME})
target_link_libraries(${LIB_DUMMIES} PUBLIC ${LIB_EIVE_MISSION}
${LIB_FSFW_NAME} ${LIB_JSON_NAME})
target_link_libraries(${OBSW_NAME} PRIVATE ${LIB_EIVE_MISSION} ${LIB_DUMMIES})

View File

@ -29,7 +29,7 @@
#include <dummies/AcuDummy.h>
#include <dummies/CoreControllerDummy.h>
#include "dummies/helpers.h"
#include "dummies/helperFactory.h"
#ifdef PLATFORM_UNIX
#include <fsfw_hal/linux/serial/SerialComIF.h>

View File

@ -20,15 +20,15 @@
#include <mission/acs/MgmRm3100CustomHandler.h>
#include <mission/acs/str/StarTrackerHandler.h>
#include <mission/acs/str/strHelpers.h>
#include <mission/com/LiveTmTask.h>
#include <mission/com/PersistentLogTmStoreTask.h>
#include <mission/com/PersistentSingleTmStoreTask.h>
#include <mission/power/CspCookie.h>
#include <mission/system/acs/ImtqAssembly.h>
#include <mission/system/acs/StrAssembly.h>
#include <mission/system/fdir/StrFdir.h>
#include <mission/system/objects/CamSwitcher.h>
#include <mission/system/objects/SyrlinksAssembly.h>
#include <mission/tmtc/LiveTmTask.h>
#include <mission/tmtc/PersistentLogTmStoreTask.h>
#include <mission/tmtc/PersistentSingleTmStoreTask.h>
#include "OBSWConfig.h"
#include "bsp_q7s/boardtest/Q7STestTask.h"
@ -81,6 +81,7 @@ using gpio::Levels;
#include <mission/acs/ImtqHandler.h>
#include <mission/acs/rwHelpers.h>
#include <mission/com/SyrlinksHandler.h>
#include <mission/com/VirtualChannelWithQueue.h>
#include <mission/payload/PayloadPcduHandler.h>
#include <mission/payload/RadiationSensorHandler.h>
#include <mission/payload/payloadPcduDefinitions.h>
@ -96,7 +97,6 @@ using gpio::Levels;
#include <mission/tcs/Max31865Definitions.h>
#include <mission/tcs/Max31865PT1000Handler.h>
#include <mission/tcs/Tmp1075Handler.h>
#include <mission/tmtc/VirtualChannelWithQueue.h>
#include <sstream>
@ -125,6 +125,7 @@ using gpio::Levels;
ResetArgs RESET_ARGS_GNSS;
std::atomic_bool LINK_STATE = CcsdsIpCoreHandler::LINK_DOWN;
std::atomic_bool PTME_LOCKED = false;
std::atomic_uint16_t I2C_FATAL_ERRORS = 0;
void Factory::setStaticFrameworkObjectIds() {
@ -754,10 +755,8 @@ ReturnValue_t ObjectFactory::createCcsdsComponents(CcsdsComponentArgs& args) {
gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_BUSY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC3, "PAPB VC3");
gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_EMPTY, gpio);
// Initialise to low and then pull high to do a PTME reset, which puts the PTME in reset
// state. It will be put out of reset in the CCSDS handler initialize function.
gpio = new GpiodRegularByLineName(q7s::gpioNames::PTME_RESETN, "PTME RESETN",
gpio::Direction::OUT, gpio::Levels::LOW);
gpio::Direction::OUT, gpio::Levels::HIGH);
gpioCookiePtmeIp->addGpio(gpioIds::PTME_RESETN, gpio);
gpioChecker(args.gpioComIF.addGpios(gpioCookiePtmeIp), "PTME PAPB VCs");
@ -791,34 +790,42 @@ ReturnValue_t ObjectFactory::createCcsdsComponents(CcsdsComponentArgs& args) {
*args.ipCoreHandler =
new CcsdsIpCoreHandler(objects::CCSDS_HANDLER, objects::CCSDS_PACKET_DISTRIBUTOR, *ptmeConfig,
LINK_STATE, &args.gpioComIF, gpios);
LINK_STATE, &args.gpioComIF, gpios, PTME_LOCKED);
// This VC will receive all live TM
auto* vcWithQueue =
new VirtualChannelWithQueue(objects::PTME_VC0_LIVE_TM, ccsds::VC0, "PTME VC0 LIVE TM", *ptme,
LINK_STATE, args.tmStore, 500);
args.liveDestination = vcWithQueue;
new LiveTmTask(objects::LIVE_TM_TASK, args.pusFunnel, args.cfdpFunnel, *vcWithQueue);
auto* liveTask = new LiveTmTask(objects::LIVE_TM_TASK, args.pusFunnel, args.cfdpFunnel,
*vcWithQueue, PTME_LOCKED);
liveTask->connectModeTreeParent(satsystem::com::SUBSYSTEM);
// Set up log store.
auto* vc = new VirtualChannel(objects::PTME_VC1_LOG_TM, ccsds::VC1, "PTME VC1 LOG TM", *ptme,
LINK_STATE);
LogStores logStores(args.stores);
// Core task which handles the LOG store and takes care of dumping it as TM using a VC directly
new PersistentLogTmStoreTask(objects::LOG_STORE_AND_TM_TASK, args.ipcStore, logStores, *vc,
*SdCardManager::instance());
auto* logStore =
new PersistentLogTmStoreTask(objects::LOG_STORE_AND_TM_TASK, args.ipcStore, logStores, *vc,
*SdCardManager::instance(), PTME_LOCKED);
logStore->connectModeTreeParent(satsystem::com::SUBSYSTEM);
vc = new VirtualChannel(objects::PTME_VC2_HK_TM, ccsds::VC2, "PTME VC2 HK TM", *ptme, LINK_STATE);
// Core task which handles the HK store and takes care of dumping it as TM using a VC directly
new PersistentSingleTmStoreTask(objects::HK_STORE_AND_TM_TASK, args.ipcStore,
*args.stores.hkStore, *vc, persTmStore::DUMP_HK_STORE_DONE,
persTmStore::DUMP_HK_STORE_DONE, *SdCardManager::instance());
auto* hkStore = new PersistentSingleTmStoreTask(
objects::HK_STORE_AND_TM_TASK, args.ipcStore, *args.stores.hkStore, *vc,
persTmStore::DUMP_HK_STORE_DONE, persTmStore::DUMP_HK_STORE_DONE, *SdCardManager::instance(),
PTME_LOCKED);
hkStore->connectModeTreeParent(satsystem::com::SUBSYSTEM);
vc = new VirtualChannel(objects::PTME_VC3_CFDP_TM, ccsds::VC3, "PTME VC3 CFDP TM", *ptme,
LINK_STATE);
// Core task which handles the CFDP store and takes care of dumping it as TM using a VC directly
new PersistentSingleTmStoreTask(objects::CFDP_STORE_AND_TM_TASK, args.ipcStore,
*args.stores.cfdpStore, *vc, persTmStore::DUMP_CFDP_STORE_DONE,
persTmStore::DUMP_CFDP_CANCELLED, *SdCardManager::instance());
auto* cfdpTask = new PersistentSingleTmStoreTask(
objects::CFDP_STORE_AND_TM_TASK, args.ipcStore, *args.stores.cfdpStore, *vc,
persTmStore::DUMP_CFDP_STORE_DONE, persTmStore::DUMP_CFDP_CANCELLED,
*SdCardManager::instance(), PTME_LOCKED);
cfdpTask->connectModeTreeParent(satsystem::com::SUBSYSTEM);
ReturnValue_t result = (*args.ipCoreHandler)->connectModeTreeParent(satsystem::com::SUBSYSTEM);
if (result != returnvalue::OK) {

View File

@ -4,11 +4,11 @@
#include <fsfw/returnvalues/returnvalue.h>
#include <fsfw_hal/linux/spi/SpiComIF.h>
#include <mission/com/CcsdsIpCoreHandler.h>
#include <mission/com/PersistentLogTmStoreTask.h>
#include <mission/genericFactory.h>
#include <mission/system/objects/Stack5VHandler.h>
#include <mission/tcs/HeaterHandler.h>
#include <mission/tmtc/CfdpTmFunnel.h>
#include <mission/tmtc/PersistentLogTmStoreTask.h>
#include <mission/tmtc/PusTmFunnel.h>
#include <atomic>
@ -24,6 +24,7 @@ class AcsBoardAssembly;
class GpioIF;
extern std::atomic_uint16_t I2C_FATAL_ERRORS;
extern std::atomic_bool PTME_LOCKED;
namespace ObjectFactory {

View File

@ -13,7 +13,7 @@
#include "bsp_q7s/core/ObjectFactory.h"
#include "busConf.h"
#include "devConf.h"
#include "dummies/helpers.h"
#include "dummies/helperFactory.h"
#include "eive/objects.h"
#include "fsfw_hal/linux/gpio/LinuxLibgpioIF.h"
#include "linux/ObjectFactory.h"

View File

@ -26,6 +26,6 @@ target_sources(
CoreControllerDummy.cpp
PlocMpsocDummy.cpp
PlocSupervisorDummy.cpp
helpers.cpp
helperFactory.cpp
MgmRm3100Dummy.cpp
Tmp1075Dummy.cpp)

View File

@ -1,4 +1,4 @@
#include "helpers.h"
#include "helperFactory.h"
#include <dummies/AcuDummy.h>
#include <dummies/BpxDummy.h>
@ -24,6 +24,7 @@
#include <dummies/StarTrackerDummy.h>
#include <dummies/SusDummy.h>
#include <dummies/SyrlinksDummy.h>
#include <fsfw/devicehandlers/HealthDevice.h>
#include <fsfw_hal/common/gpio/GpioIF.h>
#include <mission/power/gsDefs.h>
#include <mission/system/acs/ImtqAssembly.h>
@ -98,6 +99,8 @@ void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpio
new GyroAdisDummy(objects::GYRO_2_ADIS_HANDLER, objects::DUMMY_COM_IF, comCookieDummy);
assemblyDhbs[7] =
new GyroL3GD20Dummy(objects::GYRO_3_L3G_HANDLER, objects::DUMMY_COM_IF, comCookieDummy);
new HealthDevice(objects::GPS_0_HEALTH_DEV, objects::ACS_BOARD_ASS);
new HealthDevice(objects::GPS_1_HEALTH_DEV, objects::ACS_BOARD_ASS);
auto* gpsCtrl = new GpsCtrlDummy(objects::GPS_CONTROLLER);
ObjectFactory::createAcsBoardAssy(pwrSwitcher, assemblyDhbs, gpsCtrl, gpioIF);
}

View File

@ -18,29 +18,28 @@ PapbVcInterface::~PapbVcInterface() {}
ReturnValue_t PapbVcInterface::initialize() {
UioMapper uioMapper(uioFile, mapNum);
uint32_t* baseReg;
ReturnValue_t result = uioMapper.getMappedAdress(&baseReg, UioMapper::Permissions::WRITE_ONLY);
ReturnValue_t result = uioMapper.getMappedAdress(const_cast<uint32_t**>(&vcBaseReg),
UioMapper::Permissions::WRITE_ONLY);
if (result != returnvalue::OK) {
return result;
}
vcBaseReg = baseReg;
return returnvalue::OK;
}
ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size) {
if (pollPapbBusySignal(0) == returnvalue::OK) {
if (pollInterfaceReadiness(0, true) == returnvalue::OK) {
startPacketTransfer();
} else {
return DirectTmSinkIF::IS_BUSY;
}
for (size_t idx = 0; idx < size; idx++) {
// This delay is super-important, DO NOT REMOVE!
// Polling the GPIO too often can mess up the scheduler.
// Polling the GPIO or the config register too often messes up the scheduler.
// TODO: Maybe this should not be done like this. It would be better if there was a custom
// FPGA module which can accept packets and then takes care of dumping that packet into
// the PTME. DMA would be an ideal solution for this.
nanosleep(&BETWEEN_POLL_DELAY, &remDelay);
if (pollPapbBusySignal(2) == returnvalue::OK) {
if (pollInterfaceReadiness(2, false) == returnvalue::OK) {
*(vcBaseReg + DATA_REG_OFFSET) = static_cast<uint32_t>(data[idx]);
} else {
abortPacketTransfer();
@ -48,7 +47,7 @@ ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size) {
}
}
nanosleep(&BETWEEN_POLL_DELAY, &remDelay);
if (pollPapbBusySignal(2) == returnvalue::OK) {
if (pollInterfaceReadiness(2, false) == returnvalue::OK) {
completePacketTransfer();
} else {
abortPacketTransfer();
@ -61,23 +60,23 @@ void PapbVcInterface::startPacketTransfer() { *vcBaseReg = CONFIG_START; }
void PapbVcInterface::completePacketTransfer() { *vcBaseReg = CONFIG_END; }
ReturnValue_t PapbVcInterface::pollPapbBusySignal(uint32_t maxPollRetries) const {
gpio::Levels papbBusyState = gpio::Levels::LOW;
ReturnValue_t result;
ReturnValue_t PapbVcInterface::pollInterfaceReadiness(uint32_t maxPollRetries,
bool checkReadyState) const {
uint32_t busyIdx = 0;
nextDelay.tv_nsec = 0;
nextDelay.tv_nsec = FIRST_NON_NULL_DELAY_NS;
while (true) {
/** Check if PAPB interface is ready to receive data */
result = gpioComIF->readGpio(papbBusyId, papbBusyState);
if (result != returnvalue::OK) {
sif::warning << "PapbVcInterface::pollPapbBusySignal: Failed to read papb busy signal"
<< std::endl;
return returnvalue::FAILED;
}
if (papbBusyState == gpio::Levels::HIGH) {
// 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.
uint32_t reg = *vcBaseReg;
bool busy = (reg >> 5) & 0b1;
bool ready = (reg >> 6) & 0b1;
if (not busy) {
return returnvalue::OK;
}
if (checkReadyState and not ready) {
return PAPB_BUSY;
}
busyIdx++;
if (busyIdx >= maxPollRetries) {
@ -87,9 +86,7 @@ ReturnValue_t PapbVcInterface::pollPapbBusySignal(uint32_t maxPollRetries) const
// Ignore signal handling here for now.
nanosleep(&nextDelay, &remDelay);
// Adaptive delay.
if (nextDelay.tv_nsec == 0) {
nextDelay.tv_nsec = FIRST_NON_NULL_DELAY_NS;
} else if (nextDelay.tv_nsec * 2 <= MAX_DELAY_PAPB_POLLING_NS) {
if (nextDelay.tv_nsec * 2 <= MAX_DELAY_PAPB_POLLING_NS) {
nextDelay.tv_nsec *= 2;
}
}
@ -116,7 +113,7 @@ void PapbVcInterface::isVcInterfaceBufferEmpty() {
return;
}
bool PapbVcInterface::isBusy() const { return pollPapbBusySignal(0) == PAPB_BUSY; }
bool PapbVcInterface::isBusy() const { return pollInterfaceReadiness(0, true) == PAPB_BUSY; }
void PapbVcInterface::cancelTransfer() { abortPacketTransfer(); }

View File

@ -5,6 +5,8 @@
#include <fsfw_hal/linux/gpio/LinuxLibgpioIF.h>
#include <linux/ipcore/VirtualChannelIF.h>
#include <atomic>
#include "OBSWConfig.h"
#include "fsfw/returnvalues/returnvalue.h"
@ -89,7 +91,7 @@ class PapbVcInterface : public VirtualChannelIF {
std::string uioFile;
int mapNum = 0;
mutable struct timespec nextDelay = {.tv_sec = 0, .tv_nsec = 0};
const struct timespec BETWEEN_POLL_DELAY = {.tv_sec = 0, .tv_nsec = 5};
const struct timespec BETWEEN_POLL_DELAY = {.tv_sec = 0, .tv_nsec = 10};
mutable struct timespec remDelay;
volatile uint32_t* vcBaseReg = nullptr;
@ -117,7 +119,7 @@ class PapbVcInterface : public VirtualChannelIF {
*
* @return returnvalue::OK when ready to receive data else PAPB_BUSY.
*/
inline ReturnValue_t pollPapbBusySignal(uint32_t maxPollRetries) const;
inline ReturnValue_t pollInterfaceReadiness(uint32_t maxPollRetries, bool checkReadyState) const;
/**
* @brief This function can be used for debugging to check whether there are packets in

View File

@ -1,2 +1,11 @@
target_sources(${LIB_EIVE_MISSION} PRIVATE SyrlinksHandler.cpp
CcsdsIpCoreHandler.cpp)
target_sources(
${LIB_EIVE_MISSION}
PRIVATE SyrlinksHandler.cpp
CcsdsIpCoreHandler.cpp
LiveTmTask.cpp
PersistentLogTmStoreTask.cpp
TmStoreTaskBase.cpp
VirtualChannel.cpp
VirtualChannelWithQueue.cpp
PersistentSingleTmStoreTask.cpp
LiveTmTask.cpp)

View File

@ -15,9 +15,11 @@
CcsdsIpCoreHandler::CcsdsIpCoreHandler(object_id_t objectId, object_id_t tcDestination,
PtmeConfig& ptmeConfig, std::atomic_bool& linkState,
GpioIF* gpioIF, PtmeGpios gpioIds)
GpioIF* gpioIF, PtmeGpios gpioIds,
std::atomic_bool& ptmeLocked)
: SystemObject(objectId),
linkState(linkState),
ptmeLocked(ptmeLocked),
tcDestination(tcDestination),
parameterHelper(this),
actionHelper(this, nullptr),
@ -29,12 +31,14 @@ CcsdsIpCoreHandler::CcsdsIpCoreHandler(object_id_t objectId, object_id_t tcDesti
auto mqArgs = MqArgs(objectId, static_cast<void*>(this));
eventQueue =
QueueFactory::instance()->createMessageQueue(10, EventMessage::EVENT_MESSAGE_SIZE, &mqArgs);
ptmeLocked = true;
}
CcsdsIpCoreHandler::~CcsdsIpCoreHandler() = default;
ReturnValue_t CcsdsIpCoreHandler::performOperation(uint8_t operationCode) {
readCommandQueue();
performPtmeUpdateWhenApplicable();
return returnvalue::OK;
}
@ -76,6 +80,8 @@ ReturnValue_t CcsdsIpCoreHandler::initialize() {
} else {
enablePrioritySelectMode();
}
resetPtme();
ptmeLocked = false;
#if OBSW_SYRLINKS_SIMULATED == 1
// Update data on rising edge
@ -127,10 +133,15 @@ ReturnValue_t CcsdsIpCoreHandler::getParameter(uint8_t domainId, uint8_t uniqueI
return HasParametersIF::INVALID_VALUE;
}
parameterWrapper->set(batPriorityParam);
if (mode == MODE_ON) {
updateBatPriorityOnTxOff = true;
} else if (mode == MODE_OFF) {
updateBatPriorityFromParam();
if (newVal != batPriorityParam) {
// This ensures that the BAT priority is updated at some point when an update of the PTME is
// allowed
updateContext.updateBatPrio = true;
// If we are off, we can do the update after X cycles. Otherwise, wait until the transmitter
// goes off.
if (mode == MODE_OFF) {
initPtmeUpdateAfterXCycles();
}
}
return returnvalue::OK;
}
@ -148,36 +159,12 @@ ReturnValue_t CcsdsIpCoreHandler::executeAction(ActionId_t actionId, MessageQueu
const uint8_t* data, size_t size) {
ReturnValue_t result = returnvalue::OK;
switch (actionId) {
case SET_LOW_RATE: {
submode = static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_LOW);
result = ptmeConfig.setRate(RATE_100KBPS);
break;
}
case SET_HIGH_RATE: {
submode = static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_HIGH);
result = ptmeConfig.setRate(RATE_500KBPS);
break;
}
case ARBITRARY_RATE: {
uint32_t bitrate = 0;
SerializeAdapter::deSerialize(&bitrate, &data, &size, SerializeIF::Endianness::BIG);
result = ptmeConfig.setRate(bitrate);
break;
}
case EN_TRANSMITTER: {
enableTransmit();
if (mode == HasModesIF::MODE_OFF) {
mode = HasModesIF::MODE_ON;
}
return EXECUTION_FINISHED;
}
case DISABLE_TRANSMITTER: {
disableTransmit();
if (mode == HasModesIF::MODE_ON) {
mode = HasModesIF::MODE_OFF;
}
return EXECUTION_FINISHED;
}
case ENABLE_TX_CLK_MANIPULATOR: {
result = ptmeConfig.configTxManipulator(true);
break;
@ -206,12 +193,8 @@ ReturnValue_t CcsdsIpCoreHandler::executeAction(ActionId_t actionId, MessageQueu
void CcsdsIpCoreHandler::updateLinkState() { linkState = LINK_UP; }
void CcsdsIpCoreHandler::enableTransmit() {
// Reset PTME on each transmit enable.
updateBatPriorityFromParam();
#ifndef TE0720_1CFA
gpioIF->pullHigh(ptmeGpios.enableTxClock);
gpioIF->pullHigh(ptmeGpios.enableTxData);
#endif
linkState = LINK_UP;
}
@ -236,34 +219,23 @@ ReturnValue_t CcsdsIpCoreHandler::checkModeCommand(Mode_t mode, Submode_t submod
}
void CcsdsIpCoreHandler::startTransition(Mode_t mode, Submode_t submode) {
auto rateSet = [&](uint32_t rate) {
ReturnValue_t result = ptmeConfig.setRate(rate);
if (result == returnvalue::OK) {
this->mode = HasModesIF::MODE_ON;
}
};
triggerEvent(CHANGING_MODE, mode, submode);
if (mode == HasModesIF::MODE_ON) {
enableTransmit();
if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_DEFAULT)) {
com::Datarate currentDatarate = com::getCurrentDatarate();
if (currentDatarate == com::Datarate::LOW_RATE_MODULATION_BPSK) {
rateSet(RATE_100KBPS);
} else if (currentDatarate == com::Datarate::HIGH_RATE_MODULATION_0QPSK) {
rateSet(RATE_500KBPS);
}
} else if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_HIGH)) {
rateSet(RATE_500KBPS);
} else if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_LOW)) {
rateSet(RATE_100KBPS);
if (this->submode != submode) {
initPtmeUpdateAfterXCycles();
updateContext.enableTransmitAfterPtmeUpdate = true;
updateContext.updateClockRate = true;
this->submode = submode;
this->mode = mode;
updateContext.setModeAfterUpdate = true;
return;
}
// No rate change, so enable transmitter right away.
enableTransmit();
} else if (mode == HasModesIF::MODE_OFF) {
disableTransmit();
this->mode = HasModesIF::MODE_OFF;
}
this->submode = submode;
modeHelper.modeChanged(mode, submode);
announceMode(false);
setMode(mode, submode);
}
void CcsdsIpCoreHandler::announceMode(bool recursive) { triggerEvent(MODE_INFO, mode, submode); }
@ -274,9 +246,9 @@ void CcsdsIpCoreHandler::disableTransmit() {
gpioIF->pullLow(ptmeGpios.enableTxData);
#endif
linkState = LINK_DOWN;
if (updateBatPriorityOnTxOff) {
updateBatPriorityFromParam();
updateBatPriorityOnTxOff = false;
// Some parameters need update and transmitter is off now.
if (updateContext.updateBatPrio or updateContext.updateClockRate) {
initPtmeUpdateAfterXCycles();
}
}
@ -294,21 +266,9 @@ ModeTreeChildIF& CcsdsIpCoreHandler::getModeTreeChildIF() { return *this; }
object_id_t CcsdsIpCoreHandler::getObjectId() const { return SystemObject::getObjectId(); }
void CcsdsIpCoreHandler::enablePrioritySelectMode() {
ptmeConfig.enableBatPriorityBit(true);
// Reset the PTME
gpioIF->pullLow(ptmeGpios.ptmeResetn);
usleep(10);
gpioIF->pullHigh(ptmeGpios.ptmeResetn);
}
void CcsdsIpCoreHandler::enablePrioritySelectMode() { ptmeConfig.enableBatPriorityBit(true); }
void CcsdsIpCoreHandler::disablePrioritySelectMode() {
ptmeConfig.enableBatPriorityBit(false);
// Reset the PTME
gpioIF->pullLow(ptmeGpios.ptmeResetn);
usleep(10);
gpioIF->pullHigh(ptmeGpios.ptmeResetn);
}
void CcsdsIpCoreHandler::disablePrioritySelectMode() { ptmeConfig.enableBatPriorityBit(false); }
void CcsdsIpCoreHandler::updateBatPriorityFromParam() {
if (batPriorityParam == 0) {
@ -317,3 +277,79 @@ void CcsdsIpCoreHandler::updateBatPriorityFromParam() {
enablePrioritySelectMode();
}
}
void CcsdsIpCoreHandler::setMode(Mode_t mode, Submode_t submode) {
this->submode = submode;
this->mode = mode;
modeHelper.modeChanged(mode, submode);
announceMode(false);
}
void CcsdsIpCoreHandler::performPtmeUpdateWhenApplicable() {
if (not updateContext.performPtmeUpdateAfterXCycles) {
return;
}
if (updateContext.ptmeUpdateCycleCount >= 2) {
if (updateContext.updateBatPrio) {
updateBatPriorityFromParam();
updateContext.updateBatPrio = false;
}
ReturnValue_t result = returnvalue::OK;
if (updateContext.updateClockRate) {
if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_DEFAULT)) {
com::Datarate currentDatarate = com::getCurrentDatarate();
if (currentDatarate == com::Datarate::LOW_RATE_MODULATION_BPSK) {
result = ptmeConfig.setRate(RATE_100KBPS);
} else if (currentDatarate == com::Datarate::HIGH_RATE_MODULATION_0QPSK) {
result = ptmeConfig.setRate(RATE_500KBPS);
}
} else if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_HIGH)) {
result = ptmeConfig.setRate(RATE_500KBPS);
} else if (submode == static_cast<Submode_t>(com::CcsdsSubmode::DATARATE_LOW)) {
result = ptmeConfig.setRate(RATE_100KBPS);
}
if (result != returnvalue::OK) {
sif::error << "CcsdsIpCoreHandler: Setting datarate failed" << std::endl;
}
updateContext.updateClockRate = false;
}
bool doResetPtme = true;
if (not updateContext.updateBatPrio and not updateContext.updateClockRate) {
doResetPtme = false;
}
finishPtmeUpdateAfterXCycles(doResetPtme);
return;
}
updateContext.ptmeUpdateCycleCount++;
}
void CcsdsIpCoreHandler::resetPtme() {
gpioIF->pullLow(ptmeGpios.ptmeResetn);
usleep(10);
gpioIF->pullHigh(ptmeGpios.ptmeResetn);
}
void CcsdsIpCoreHandler::initPtmeUpdateAfterXCycles() {
if (not updateContext.performPtmeUpdateAfterXCycles) {
updateContext.performPtmeUpdateAfterXCycles = true;
updateContext.ptmeUpdateCycleCount = 0;
ptmeLocked = true;
}
}
void CcsdsIpCoreHandler::finishPtmeUpdateAfterXCycles(bool doResetPtme) {
if (doResetPtme) {
resetPtme();
}
ptmeLocked = false;
updateContext.performPtmeUpdateAfterXCycles = false;
updateContext.ptmeUpdateCycleCount = 0;
if (updateContext.enableTransmitAfterPtmeUpdate) {
enableTransmit();
updateContext.enableTransmitAfterPtmeUpdate = false;
}
if (updateContext.setModeAfterUpdate) {
setMode(mode, submode);
updateContext.setModeAfterUpdate = false;
}
}

View File

@ -2,7 +2,7 @@
#define CCSDSHANDLER_H_
#include <fsfw/modes/HasModesIF.h>
#include <mission/tmtc/VirtualChannelWithQueue.h>
#include <mission/com/VirtualChannelWithQueue.h>
#include <cstdint>
#include <unordered_map>
@ -79,7 +79,8 @@ class CcsdsIpCoreHandler : public SystemObject,
* @param enTxData GPIO ID of RS485 tx data enable
*/
CcsdsIpCoreHandler(object_id_t objectId, object_id_t tcDestination, PtmeConfig& ptmeConfig,
std::atomic_bool& linkState, GpioIF* gpioIF, PtmeGpios gpioIds);
std::atomic_bool& linkState, GpioIF* gpioIF, PtmeGpios gpioIds,
std::atomic_bool& ptmeLocked);
~CcsdsIpCoreHandler();
@ -137,9 +138,8 @@ class CcsdsIpCoreHandler : public SystemObject,
//! [EXPORT] : [COMMENT] Received action message with unknown action id
static const ReturnValue_t COMMAND_NOT_IMPLEMENTED = MAKE_RETURN_CODE(0xA0);
// using VirtualChannelMap = std::unordered_map<VcId_t, VirtualChannelWithQueue*>;
// VirtualChannelMap virtualChannelMap;
std::atomic_bool& linkState;
std::atomic_bool& ptmeLocked;
object_id_t tcDestination;
@ -158,7 +158,15 @@ class CcsdsIpCoreHandler : public SystemObject,
PtmeGpios ptmeGpios;
// BAT priority bit on by default to enable priority selection mode for the PTME.
uint8_t batPriorityParam = 0;
bool updateBatPriorityOnTxOff = false;
struct UpdateContext {
bool updateBatPrio = false;
bool updateClockRate = false;
bool enableTransmitAfterPtmeUpdate = false;
uint8_t ptmeUpdateCycleCount = 0;
bool performPtmeUpdateAfterXCycles = false;
bool setModeAfterUpdate = false;
} updateContext{};
GpioIF* gpioIF = nullptr;
@ -180,6 +188,8 @@ class CcsdsIpCoreHandler : public SystemObject,
*/
void disableTransmit();
void performPtmeUpdateWhenApplicable();
/**
* The following set of functions configure the mode of the PTME bandwith allocation table (BAT)
* module. This consists of the following 2 steps:
@ -189,6 +199,11 @@ class CcsdsIpCoreHandler : public SystemObject,
void enablePrioritySelectMode();
void disablePrioritySelectMode();
void updateBatPriorityFromParam();
void setMode(Mode_t mode, Submode_t submode);
void resetPtme();
void initPtmeUpdateAfterXCycles();
void finishPtmeUpdateAfterXCycles(bool doResetPtme);
};
#endif /* CCSDSHANDLER_H_ */

107
mission/com/LiveTmTask.cpp Normal file
View File

@ -0,0 +1,107 @@
#include "LiveTmTask.h"
#include <fsfw/ipc/QueueFactory.h>
#include <fsfw/subsystem/helper.h>
#include <fsfw/tasks/TaskFactory.h>
#include <fsfw/timemanager/Stopwatch.h>
LiveTmTask::LiveTmTask(object_id_t objectId, PusTmFunnel& pusFunnel, CfdpTmFunnel& cfdpFunnel,
VirtualChannelWithQueue& channel, const std::atomic_bool& ptmeLocked)
: SystemObject(objectId),
modeHelper(this),
pusFunnel(pusFunnel),
cfdpFunnel(cfdpFunnel),
channel(channel),
ptmeLocked(ptmeLocked) {
requestQueue = QueueFactory::instance()->createMessageQueue();
}
ReturnValue_t LiveTmTask::performOperation(uint8_t opCode) {
readCommandQueue();
while (true) {
bool performWriteOp = true;
if (mode == MODE_OFF or ptmeLocked) {
performWriteOp = false;
}
// The funnel tasks are scheduled here directly as well.
ReturnValue_t result = channel.handleNextTm(performWriteOp);
if (result == DirectTmSinkIF::IS_BUSY) {
sif::error << "Lost live TM, PAPB busy" << std::endl;
}
if (result == MessageQueueIF::EMPTY) {
if (tmFunnelCd.hasTimedOut()) {
pusFunnel.performOperation(0);
cfdpFunnel.performOperation(0);
tmFunnelCd.resetTimer();
}
// Read command queue during idle times.
readCommandQueue();
// 40 ms IDLE delay. Might tweak this in the future.
TaskFactory::delayTask(40);
} else {
packetCounter++;
}
}
}
MessageQueueId_t LiveTmTask::getCommandQueue() const { return requestQueue->getId(); }
void LiveTmTask::getMode(Mode_t* mode, Submode_t* submode) {
if (mode != nullptr) {
*mode = this->mode;
}
if (submode != nullptr) {
*submode = SUBMODE_NONE;
}
}
ReturnValue_t LiveTmTask::checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t* msToReachTheMode) {
if (mode == MODE_ON or mode == MODE_OFF) {
return returnvalue::OK;
}
return returnvalue::FAILED;
}
void LiveTmTask::startTransition(Mode_t mode, Submode_t submode) {
this->mode = mode;
modeHelper.modeChanged(mode, submode);
announceMode(false);
}
void LiveTmTask::announceMode(bool recursive) { triggerEvent(MODE_INFO, mode, SUBMODE_NONE); }
object_id_t LiveTmTask::getObjectId() const { return SystemObject::getObjectId(); }
const HasHealthIF* LiveTmTask::getOptHealthIF() const { return nullptr; }
const HasModesIF& LiveTmTask::getModeIF() const { return *this; }
ReturnValue_t LiveTmTask::connectModeTreeParent(HasModeTreeChildrenIF& parent) {
return modetree::connectModeTreeParent(parent, *this, nullptr, modeHelper);
}
void LiveTmTask::readCommandQueue(void) {
CommandMessage commandMessage;
ReturnValue_t result = returnvalue::FAILED;
result = requestQueue->receiveMessage(&commandMessage);
if (result == returnvalue::OK) {
result = modeHelper.handleModeCommand(&commandMessage);
if (result == returnvalue::OK) {
return;
}
CommandMessage reply;
reply.setReplyRejected(CommandMessage::UNKNOWN_COMMAND, commandMessage.getCommand());
requestQueue->reply(&reply);
return;
}
}
ModeTreeChildIF& LiveTmTask::getModeTreeChildIF() { return *this; }
ReturnValue_t LiveTmTask::initialize() {
modeHelper.initialize();
return returnvalue::OK;
}

57
mission/com/LiveTmTask.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef MISSION_TMTC_LIVETMTASK_H_
#define MISSION_TMTC_LIVETMTASK_H_
#include <fsfw/modes/HasModesIF.h>
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/subsystem/ModeTreeChildIF.h>
#include <fsfw/subsystem/ModeTreeConnectionIF.h>
#include <fsfw/tasks/ExecutableObjectIF.h>
#include <fsfw/timemanager/Countdown.h>
#include <mission/com/VirtualChannelWithQueue.h>
#include <mission/tmtc/CfdpTmFunnel.h>
#include <mission/tmtc/PusTmFunnel.h>
class LiveTmTask : public SystemObject,
public HasModesIF,
public ExecutableObjectIF,
public ModeTreeChildIF,
public ModeTreeConnectionIF {
public:
LiveTmTask(object_id_t objectId, PusTmFunnel& pusFunnel, CfdpTmFunnel& cfdpFunnel,
VirtualChannelWithQueue& channel, const std::atomic_bool& ptmeLocked);
ReturnValue_t performOperation(uint8_t opCode) override;
ReturnValue_t initialize() override;
ReturnValue_t connectModeTreeParent(HasModeTreeChildrenIF& parent) override;
private:
MessageQueueIF* requestQueue;
ModeHelper modeHelper;
Mode_t mode = HasModesIF::MODE_OFF;
Countdown tmFunnelCd = Countdown(100);
PusTmFunnel& pusFunnel;
CfdpTmFunnel& cfdpFunnel;
VirtualChannelWithQueue& channel;
uint32_t packetCounter = 0;
const std::atomic_bool& ptmeLocked;
void readCommandQueue(void);
MessageQueueId_t getCommandQueue() const override;
void getMode(Mode_t* mode, Submode_t* submode) override;
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t* msToReachTheMode) override;
void startTransition(Mode_t mode, Submode_t submode) override;
void announceMode(bool recursive) override;
object_id_t getObjectId() const override;
const HasHealthIF* getOptHealthIF() const override;
const HasModesIF& getModeIF() const override;
ModeTreeChildIF& getModeTreeChildIF() override;
};
#endif /* MISSION_TMTC_LIVETMTASK_H_ */

View File

@ -5,8 +5,9 @@
PersistentLogTmStoreTask::PersistentLogTmStoreTask(object_id_t objectId, StorageManagerIF& ipcStore,
LogStores stores, VirtualChannel& channel,
SdCardMountedIF& sdcMan)
: TmStoreTaskBase(objectId, ipcStore, channel, sdcMan),
SdCardMountedIF& sdcMan,
const std::atomic_bool& ptmeLocked)
: TmStoreTaskBase(objectId, ipcStore, channel, sdcMan, ptmeLocked),
stores(stores),
okStoreContext(persTmStore::DUMP_OK_STORE_DONE, persTmStore::DUMP_OK_CANCELLED),
notOkStoreContext(persTmStore::DUMP_NOK_STORE_DONE, persTmStore::DUMP_NOK_CANCELLED),
@ -27,6 +28,8 @@ ReturnValue_t PersistentLogTmStoreTask::performOperation(uint8_t opCode) {
}
};
while (true) {
readCommandQueue();
if (not cyclicStoreCheck()) {
continue;
}
@ -54,3 +57,17 @@ bool PersistentLogTmStoreTask::initStoresIfPossible() {
}
return false;
}
void PersistentLogTmStoreTask::startTransition(Mode_t mode, Submode_t submode) {
if (mode == MODE_OFF) {
bool channelIsOn = channel.isTxOn();
cancelDump(okStoreContext, stores.okStore, channelIsOn);
cancelDump(notOkStoreContext, stores.notOkStore, channelIsOn);
cancelDump(miscStoreContext, stores.miscStore, channelIsOn);
this->mode = MODE_OFF;
} else if (mode == MODE_ON) {
this->mode = MODE_ON;
}
modeHelper.modeChanged(mode, submode);
announceMode(false);
}

View File

@ -5,11 +5,11 @@
#include <fsfw/storagemanager/StorageManagerIF.h>
#include <fsfw/tasks/ExecutableObjectIF.h>
#include <fsfw/tmtcservices/AcceptsTelemetryIF.h>
#include <mission/com/TmStoreTaskBase.h>
#include <mission/com/VirtualChannelWithQueue.h>
#include <mission/genericFactory.h>
#include <mission/tmtc/PersistentTmStore.h>
#include <mission/tmtc/PersistentTmStoreWithTmQueue.h>
#include <mission/tmtc/TmStoreTaskBase.h>
#include <mission/tmtc/VirtualChannelWithQueue.h>
struct LogStores {
LogStores(PersistentTmStores& stores)
@ -22,7 +22,8 @@ struct LogStores {
class PersistentLogTmStoreTask : public TmStoreTaskBase, public ExecutableObjectIF {
public:
PersistentLogTmStoreTask(object_id_t objectId, StorageManagerIF& ipcStore, LogStores tmStore,
VirtualChannel& channel, SdCardMountedIF& sdcMan);
VirtualChannel& channel, SdCardMountedIF& sdcMan,
const std::atomic_bool& ptmeLocked);
ReturnValue_t performOperation(uint8_t opCode) override;
@ -36,6 +37,7 @@ class PersistentLogTmStoreTask : public TmStoreTaskBase, public ExecutableObject
bool someFileWasSwapped = false;
bool initStoresIfPossible() override;
void startTransition(Mode_t mode, Submode_t submode) override;
};
#endif /* MISSION_TMTC_PERSISTENTLOGTMSTORETASK_H_ */

View File

@ -1,17 +1,21 @@
#include "PersistentSingleTmStoreTask.h"
#include <fsfw/tasks/TaskFactory.h>
#include <fsfw/timemanager/Stopwatch.h>
#include <mission/tmtc/PersistentSingleTmStoreTask.h>
#include <unistd.h>
PersistentSingleTmStoreTask::PersistentSingleTmStoreTask(
object_id_t objectId, StorageManagerIF& ipcStore, PersistentTmStoreWithTmQueue& tmStore,
VirtualChannel& channel, Event eventIfDumpDone, Event eventIfCancelled, SdCardMountedIF& sdcMan)
: TmStoreTaskBase(objectId, ipcStore, channel, sdcMan),
VirtualChannel& channel, Event eventIfDumpDone, Event eventIfCancelled, SdCardMountedIF& sdcMan,
const std::atomic_bool& ptmeLocked)
: TmStoreTaskBase(objectId, ipcStore, channel, sdcMan, ptmeLocked),
storeWithQueue(tmStore),
dumpContext(eventIfDumpDone, eventIfCancelled) {}
ReturnValue_t PersistentSingleTmStoreTask::performOperation(uint8_t opCode) {
while (true) {
readCommandQueue();
// Delay done by the check
if (not cyclicStoreCheck()) {
continue;
@ -22,6 +26,9 @@ ReturnValue_t PersistentSingleTmStoreTask::performOperation(uint8_t opCode) {
} else if (dumpContext.vcBusyDuringDump) {
// TODO: Might not be necessary
TaskFactory::delayTask(10);
} else {
// TODO: Would be best to remove this, but not delaying here can lead to evil issues.
TaskFactory::delayTask(10);
}
}
}
@ -33,3 +40,14 @@ bool PersistentSingleTmStoreTask::initStoresIfPossible() {
}
return false;
}
void PersistentSingleTmStoreTask::startTransition(Mode_t mode, Submode_t submode) {
if (mode == MODE_OFF) {
cancelDump(dumpContext, storeWithQueue, channel.isTxOn());
this->mode = MODE_OFF;
} else if (mode == MODE_ON) {
this->mode = MODE_ON;
}
modeHelper.modeChanged(mode, submode);
announceMode(false);
}

View File

@ -3,16 +3,16 @@
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/tasks/ExecutableObjectIF.h>
#include <mission/com/TmStoreTaskBase.h>
#include <mission/com/VirtualChannel.h>
#include <mission/tmtc/PersistentTmStoreWithTmQueue.h>
#include <mission/tmtc/TmStoreTaskBase.h>
#include <mission/tmtc/VirtualChannel.h>
class PersistentSingleTmStoreTask : public TmStoreTaskBase, public ExecutableObjectIF {
public:
PersistentSingleTmStoreTask(object_id_t objectId, StorageManagerIF& ipcStore,
PersistentTmStoreWithTmQueue& storeWithQueue, VirtualChannel& channel,
Event eventIfDumpDone, Event eventIfCancelled,
SdCardMountedIF& sdcMan);
SdCardMountedIF& sdcMan, const std::atomic_bool& ptmeLocked);
ReturnValue_t performOperation(uint8_t opCode) override;
@ -23,6 +23,8 @@ class PersistentSingleTmStoreTask : public TmStoreTaskBase, public ExecutableObj
Countdown graceDelayDuringDumping = Countdown(100);
bool initStoresIfPossible() override;
void startTransition(Mode_t mode, Submode_t submode) override;
};
#endif /* MISSION_TMTC_PERSISTENTSINGLETMSTORETASK_H_ */

View File

@ -21,6 +21,7 @@ SyrlinksHandler::~SyrlinksHandler() = default;
void SyrlinksHandler::doStartUp() {
if (internalState == InternalState::OFF) {
transitionCommandPending = false;
internalState = InternalState::ENABLE_TEMPERATURE_PROTECTION;
commandExecuted = false;
}
@ -38,6 +39,7 @@ void SyrlinksHandler::doShutDown() {
// In any case, always disable TX first.
if (internalState != InternalState::SET_TX_STANDBY) {
internalState = InternalState::SET_TX_STANDBY;
transitionCommandPending = false;
commandExecuted = false;
}
if (internalState == InternalState::SET_TX_STANDBY) {
@ -122,8 +124,9 @@ ReturnValue_t SyrlinksHandler::buildTransitionDeviceCommand(DeviceCommandId_t* i
*id = syrlinks::SET_TX_MODE_STANDBY;
return buildCommandFromCommand(*id, nullptr, 0);
}
default:
default: {
break;
}
}
return NOTHING_TO_SEND;
}
@ -442,7 +445,6 @@ ReturnValue_t SyrlinksHandler::interpretDeviceReply(DeviceCommandId_t id, const
return DeviceHandlerIF::UNKNOWN_DEVICE_REPLY;
}
}
return returnvalue::OK;
}
@ -682,6 +684,9 @@ ReturnValue_t SyrlinksHandler::handleAckReply(const uint8_t* packet) {
}
break;
}
default: {
sif::error << "Syrlinks: Unexpected ACK reply" << std::endl;
}
}
switch (rememberCommandId) {
case (syrlinks::SET_TX_MODE_STANDBY): {
@ -728,16 +733,19 @@ void SyrlinksHandler::doTransition(Mode_t modeFrom, Submode_t subModeFrom) {
Mode_t tgtMode = getBaseMode(getMode());
auto commandDone = [&]() {
setMode(tgtMode);
transitionCommandPending = false;
internalState = InternalState::IDLE;
};
auto txOnHandler = [&](InternalState selMod) {
if (internalState == InternalState::IDLE) {
transitionCommandPending = false;
commandExecuted = false;
internalState = selMod;
}
// Select modulation first (BPSK or 0QPSK).
if (internalState == selMod) {
if (commandExecuted) {
transitionCommandPending = false;
internalState = InternalState::SET_TX_MODULATION;
commandExecuted = false;
}
@ -753,6 +761,7 @@ void SyrlinksHandler::doTransition(Mode_t modeFrom, Submode_t subModeFrom) {
};
auto txStandbyHandler = [&]() {
if (internalState == InternalState::IDLE) {
transitionCommandPending = false;
internalState = InternalState::SET_TX_STANDBY;
commandExecuted = false;
}

View File

@ -112,6 +112,7 @@ class SyrlinksHandler : public DeviceHandlerBase {
float tempPowerAmplifier = 0;
float tempBasebandBoard = 0;
bool commandExecuted = false;
bool transitionCommandPending = false;
uint8_t commandBuffer[syrlinks::MAX_COMMAND_SIZE];

View File

@ -0,0 +1,228 @@
#include "TmStoreTaskBase.h"
#include <fsfw/ipc/CommandMessageIF.h>
#include <fsfw/ipc/QueueFactory.h>
#include <fsfw/subsystem/helper.h>
#include <fsfw/tasks/TaskFactory.h>
#include <fsfw/timemanager/Stopwatch.h>
#include <fsfw/tmstorage/TmStoreMessage.h>
#include "mission/persistentTmStoreDefs.h"
TmStoreTaskBase::TmStoreTaskBase(object_id_t objectId, StorageManagerIF& ipcStore,
VirtualChannel& channel, SdCardMountedIF& sdcMan,
const std::atomic_bool& ptmeLocked)
: SystemObject(objectId),
modeHelper(this),
ipcStore(ipcStore),
tmReader(&timeReader),
channel(channel),
sdcMan(sdcMan),
ptmeLocked(ptmeLocked) {
requestQueue = QueueFactory::instance()->createMessageQueue(10);
}
bool TmStoreTaskBase::handleOneStore(PersistentTmStoreWithTmQueue& store,
DumpContext& dumpContext) {
ReturnValue_t result;
bool tmToStoreReceived = false;
bool tcRequestReceived = false;
bool dumpPerformed = false;
fileHasSwapped = false;
dumpContext.packetWasDumped = false;
dumpContext.vcBusyDuringDump = false;
// Store TM persistently
result = store.handleNextTm();
if (result == returnvalue::OK) {
tmToStoreReceived = true;
}
// Dump TMs
if (store.getState() == PersistentTmStore::State::DUMPING) {
if (handleOneDump(store, dumpContext, dumpPerformed) != returnvalue::OK) {
return result;
}
} else {
Command_t execCmd;
// Handle TC requests, for example deletion or retrieval requests.
result = store.handleCommandQueue(ipcStore, execCmd);
if (result == returnvalue::OK) {
if (execCmd == TmStoreMessage::DOWNLINK_STORE_CONTENT_TIME) {
cancelDumpCd.resetTimer();
tmSinkBusyCd.resetTimer();
dumpContext.reset();
}
tcRequestReceived = true;
}
}
if (tcRequestReceived or tmToStoreReceived or dumpPerformed) {
return true;
}
return false;
}
bool TmStoreTaskBase::cyclicStoreCheck() {
if (not storesInitialized) {
storesInitialized = initStoresIfPossible();
if (not storesInitialized) {
TaskFactory::delayTask(400);
return false;
}
} else if (sdCardCheckCd.hasTimedOut()) {
if (not sdcMan.isSdCardUsable(std::nullopt)) {
// Might be due to imminent shutdown or SD card switch.
storesInitialized = false;
TaskFactory::delayTask(100);
return false;
}
sdCardCheckCd.resetTimer();
}
return true;
}
void TmStoreTaskBase::cancelDump(DumpContext& ctx, PersistentTmStore& store, bool isTxOn) {
ctx.reset();
if (store.getState() == PersistentTmStore::State::DUMPING) {
triggerEvent(ctx.eventIfCancelled, ctx.numberOfDumpedPackets, ctx.dumpedBytes);
}
store.cancelDump();
if (isTxOn) {
channel.cancelTransfer();
}
}
ReturnValue_t TmStoreTaskBase::handleOneDump(PersistentTmStoreWithTmQueue& store,
DumpContext& dumpContext, bool& dumpPerformed) {
ReturnValue_t result = returnvalue::OK;
// The PTME might have been reset an transmitter state change, so there is no point in continuing
// the dump.
// TODO: Will be solved in a cleaner way, this is kind of a hack.
if (not channel.isTxOn()) {
cancelDump(dumpContext, store, false);
return returnvalue::FAILED;
}
// It is assumed that the PTME will only be locked for a short period (e.g. to change datarate).
if (not channel.isBusy() and not ptmeLocked) {
performDump(store, dumpContext, dumpPerformed);
} else {
// The PTME might be at full load, so it might sense to delay for a bit to let it do
// its work until some more bandwidth is available. Set a flag here so the upper layer can
// do ths.
dumpContext.vcBusyDuringDump = true;
dumpContext.ptmeBusyCounter++;
if (dumpContext.ptmeBusyCounter == 100) {
// If this happens, something is probably wrong.
sif::warning << "PTME busy for longer period. Cancelling dump" << std::endl;
cancelDump(dumpContext, store, channel.isTxOn());
}
}
if (cancelDumpCd.hasTimedOut() or tmSinkBusyCd.hasTimedOut()) {
cancelDump(dumpContext, store, channel.isTxOn());
}
return result;
}
ReturnValue_t TmStoreTaskBase::performDump(PersistentTmStoreWithTmQueue& store,
DumpContext& dumpContext, bool& dumpPerformed) {
size_t dumpedLen = 0;
auto dumpDoneHandler = [&]() {
uint32_t startTime;
uint32_t endTime;
store.getStartAndEndTimeCurrentOrLastDump(startTime, endTime);
triggerEvent(dumpContext.eventIfDone, dumpContext.numberOfDumpedPackets,
dumpContext.dumpedBytes);
dumpContext.reset();
};
// Dump the next packet into the PTME.
dumpContext.ptmeBusyCounter = 0;
tmSinkBusyCd.resetTimer();
ReturnValue_t result = store.getNextDumpPacket(tmReader, fileHasSwapped);
if (result != returnvalue::OK) {
sif::error << "PersistentTmStore: Getting next dump packet failed" << std::endl;
} else if (fileHasSwapped or result == PersistentTmStore::DUMP_DONE) {
// This can happen if a file is corrupted and the next file swap completes the dump.
dumpDoneHandler();
return returnvalue::OK;
}
dumpedLen = tmReader.getFullPacketLen();
// Only write to VC if mode is on, but always confirm the dump.
// If the mode is OFF, it is assumed the PTME is not usable and is not allowed to be written
// (e.g. to confirm a reset or the transmitter is off anyway).
if (mode == MODE_ON) {
result = channel.write(tmReader.getFullData(), dumpedLen);
if (result == DirectTmSinkIF::IS_BUSY) {
sif::warning << "PersistentTmStore: Unexpected VC channel busy" << std::endl;
} else if (result != returnvalue::OK) {
sif::warning << "PersistentTmStore: Unexpected VC channel write failure" << std::endl;
}
}
result = store.confirmDump(tmReader, fileHasSwapped);
if ((result == PersistentTmStore::DUMP_DONE or result == returnvalue::OK)) {
dumpPerformed = true;
if (dumpedLen > 0) {
dumpContext.dumpedBytes += dumpedLen;
dumpContext.numberOfDumpedPackets += 1;
dumpContext.packetWasDumped = true;
}
}
if (result == PersistentTmStore::DUMP_DONE) {
dumpDoneHandler();
}
return returnvalue::OK;
}
ReturnValue_t TmStoreTaskBase::initialize() {
modeHelper.initialize();
return returnvalue::OK;
}
void TmStoreTaskBase::getMode(Mode_t* mode, Submode_t* submode) {
if (mode != nullptr) {
*mode = this->mode;
}
if (submode != nullptr) {
*submode = SUBMODE_NONE;
}
}
ReturnValue_t TmStoreTaskBase::checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t* msToReachTheMode) {
if (mode == MODE_ON or mode == MODE_OFF) {
return returnvalue::OK;
}
return returnvalue::FAILED;
}
MessageQueueId_t TmStoreTaskBase::getCommandQueue() const { return requestQueue->getId(); }
void TmStoreTaskBase::announceMode(bool recursive) { triggerEvent(MODE_INFO, mode, SUBMODE_NONE); }
object_id_t TmStoreTaskBase::getObjectId() const { return SystemObject::getObjectId(); }
const HasHealthIF* TmStoreTaskBase::getOptHealthIF() const { return nullptr; }
const HasModesIF& TmStoreTaskBase::getModeIF() const { return *this; }
ReturnValue_t TmStoreTaskBase::connectModeTreeParent(HasModeTreeChildrenIF& parent) {
return modetree::connectModeTreeParent(parent, *this, nullptr, modeHelper);
}
ModeTreeChildIF& TmStoreTaskBase::getModeTreeChildIF() { return *this; }
void TmStoreTaskBase::readCommandQueue(void) {
CommandMessage commandMessage;
ReturnValue_t result = returnvalue::FAILED;
result = requestQueue->receiveMessage(&commandMessage);
if (result == returnvalue::OK) {
result = modeHelper.handleModeCommand(&commandMessage);
if (result == returnvalue::OK) {
return;
}
CommandMessage reply;
reply.setReplyRejected(CommandMessage::UNKNOWN_COMMAND, commandMessage.getCommand());
requestQueue->reply(&reply);
return;
}
}

View File

@ -1,15 +1,22 @@
#ifndef MISSION_TMTC_TMSTORETASKBASE_H_
#define MISSION_TMTC_TMSTORETASKBASE_H_
#include <fsfw/modes/HasModesIF.h>
#include <fsfw/subsystem/ModeTreeChildIF.h>
#include <fsfw/subsystem/ModeTreeConnectionIF.h>
#include <fsfw/timemanager/CdsShortTimeStamper.h>
#include <fsfw/timemanager/Countdown.h>
#include <mission/com/VirtualChannel.h>
#include <mission/tmtc/PersistentTmStoreWithTmQueue.h>
#include <mission/tmtc/VirtualChannel.h>
/**
* Generic class which composes a Virtual Channel and a persistent TM stores. This allows dumping
* the TM store into the virtual channel directly.
*/
class TmStoreTaskBase : public SystemObject {
class TmStoreTaskBase : public SystemObject,
public HasModesIF,
public ModeTreeChildIF,
public ModeTreeConnectionIF {
public:
struct DumpContext {
DumpContext(Event eventIfDone, Event eventIfCancelled)
@ -33,19 +40,35 @@ class TmStoreTaskBase : public SystemObject {
};
TmStoreTaskBase(object_id_t objectId, StorageManagerIF& ipcStore, VirtualChannel& channel,
SdCardMountedIF& sdcMan);
SdCardMountedIF& sdcMan, const std::atomic_bool& ptmeLocked);
ReturnValue_t initialize() override;
ReturnValue_t connectModeTreeParent(HasModeTreeChildrenIF& parent) override;
protected:
ModeHelper modeHelper;
MessageQueueIF* requestQueue;
StorageManagerIF& ipcStore;
PusTmReader tmReader;
CdsShortTimeStamper timeReader;
VirtualChannel& channel;
SdCardMountedIF& sdcMan;
const std::atomic_bool& ptmeLocked;
Mode_t mode = HasModesIF::MODE_OFF;
Countdown sdCardCheckCd = Countdown(800);
// 20 minutes are allowed as maximum dump time.
Countdown cancelDumpCd = Countdown(60 * 20 * 1000);
// If the TM sink is busy for 1 minute for whatever reason, cancel the dump.
Countdown tmSinkBusyCd = Countdown(60 * 1000);
VirtualChannel& channel;
bool storesInitialized = false;
bool fileHasSwapped = false;
SdCardMountedIF& sdcMan;
void readCommandQueue(void);
virtual bool initStoresIfPossible() = 0;
virtual void startTransition(Mode_t mode, Submode_t submode) = 0;
void cancelDump(DumpContext& ctx, PersistentTmStore& store, bool isTxOn);
/**
@ -58,6 +81,8 @@ class TmStoreTaskBase : public SystemObject {
ReturnValue_t handleOneDump(PersistentTmStoreWithTmQueue& store, DumpContext& dumpContext,
bool& dumpPerformed);
ReturnValue_t performDump(PersistentTmStoreWithTmQueue& store, DumpContext& dumpContext,
bool& dumpPerformed);
/**
* Occasionally check whether SD card is okay to be used. If not, poll whether it is ready to
@ -65,7 +90,17 @@ class TmStoreTaskBase : public SystemObject {
*/
bool cyclicStoreCheck();
virtual bool initStoresIfPossible() = 0;
MessageQueueId_t getCommandQueue() const override;
void getMode(Mode_t* mode, Submode_t* submode) override;
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t* msToReachTheMode) override;
void announceMode(bool recursive) override;
object_id_t getObjectId() const override;
const HasHealthIF* getOptHealthIF() const override;
const HasModesIF& getModeIF() const override;
ModeTreeChildIF& getModeTreeChildIF() override;
};
#endif /* MISSION_TMTC_TMSTORETASKBASE_H_ */

View File

@ -1,4 +1,4 @@
#include <mission/tmtc/VirtualChannelWithQueue.h>
#include "VirtualChannelWithQueue.h"
#include "OBSWConfig.h"
#include "fsfw/ipc/QueueFactory.h"
@ -19,7 +19,7 @@ VirtualChannelWithQueue::VirtualChannelWithQueue(object_id_t objectId, uint8_t v
const char* VirtualChannelWithQueue::getName() const { return VirtualChannel::getName(); }
ReturnValue_t VirtualChannelWithQueue::sendNextTm() {
ReturnValue_t VirtualChannelWithQueue::handleNextTm(bool performWriteOp) {
TmTcMessage message;
ReturnValue_t result = tmQueue->receiveMessage(&message);
if (result == MessageQueueIF::EMPTY) {
@ -36,7 +36,9 @@ ReturnValue_t VirtualChannelWithQueue::sendNextTm() {
return result;
}
result = write(data, size);
if (performWriteOp) {
result = write(data, size);
}
// Try delete in any case, ignore failures (which should not happen), it is more important to
// propagate write errors.
tmStore.deleteData(storeId);

View File

@ -4,7 +4,7 @@
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/tasks/ExecutableObjectIF.h>
#include <linux/ipcore/PtmeIF.h>
#include <mission/tmtc/VirtualChannel.h>
#include <mission/com/VirtualChannel.h>
#include <atomic>
@ -34,7 +34,7 @@ class VirtualChannelWithQueue : public VirtualChannel, public AcceptsTelemetryIF
MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel = 0) const override;
[[nodiscard]] const char* getName() const override;
ReturnValue_t sendNextTm();
ReturnValue_t handleNextTm(bool performWriteOp);
private:
MessageQueueIF* tmQueue = nullptr;

View File

@ -3,6 +3,8 @@
#include <fsfw/ipc/MutexFactory.h>
#include <fsfw/ipc/MutexGuard.h>
#include <atomic>
com::Datarate DATARATE_CFG_RAW = com::Datarate::LOW_RATE_MODULATION_BPSK;
MutexIF* DATARATE_LOCK = nullptr;

View File

@ -26,10 +26,12 @@ ReturnValue_t pst::pstSyrlinks(FixedTimeslotTaskIF *thisSequence) {
thisSequence->addSlot(objects::SYRLINKS_HANDLER, length * 0, DeviceHandlerIF::GET_WRITE);
thisSequence->addSlot(objects::SYRLINKS_HANDLER, length * 0.25, DeviceHandlerIF::SEND_READ);
thisSequence->addSlot(objects::SYRLINKS_HANDLER, length * 0.25, DeviceHandlerIF::GET_READ);
thisSequence->addSlot(objects::SYRLINKS_HANDLER, length * 0.4,
DeviceHandlerIF::PERFORM_OPERATION);
thisSequence->addSlot(objects::SYRLINKS_HANDLER, length * 0.4, DeviceHandlerIF::SEND_WRITE);
thisSequence->addSlot(objects::SYRLINKS_HANDLER, length * 0.4, DeviceHandlerIF::GET_WRITE);
thisSequence->addSlot(objects::SYRLINKS_HANDLER, length * 0.7, DeviceHandlerIF::SEND_READ);
thisSequence->addSlot(objects::SYRLINKS_HANDLER, length * 0.7, DeviceHandlerIF::GET_READ);
thisSequence->addSlot(objects::SYRLINKS_HANDLER, length * 0.75, DeviceHandlerIF::SEND_READ);
thisSequence->addSlot(objects::SYRLINKS_HANDLER, length * 0.75, DeviceHandlerIF::GET_READ);
static_cast<void>(length);

View File

@ -22,7 +22,7 @@ auto COM_SEQUENCE_RX_ONLY =
auto COM_TABLE_RX_ONLY_TGT = std::make_pair(
static_cast<uint32_t>(::com::Submode::RX_ONLY << 24) | 1, FixedArrayList<ModeListEntry, 3>());
auto COM_TABLE_RX_ONLY_TRANS_0 = std::make_pair(
static_cast<uint32_t>(::com::Submode::RX_ONLY << 24) | 2, FixedArrayList<ModeListEntry, 3>());
static_cast<uint32_t>(::com::Submode::RX_ONLY << 24) | 2, FixedArrayList<ModeListEntry, 6>());
auto COM_TABLE_RX_ONLY_TRANS_1 = std::make_pair(
static_cast<uint32_t>(::com::Submode::RX_ONLY << 24) | 3, FixedArrayList<ModeListEntry, 3>());
@ -36,7 +36,7 @@ auto COM_TABLE_RX_AND_TX_LOW_RATE_TRANS_0 =
FixedArrayList<ModeListEntry, 3>());
auto COM_TABLE_RX_AND_TX_LOW_RATE_TRANS_1 =
std::make_pair(static_cast<uint32_t>(::com::Submode::RX_AND_TX_LOW_DATARATE << 24) | 3,
FixedArrayList<ModeListEntry, 3>());
FixedArrayList<ModeListEntry, 6>());
auto COM_SEQUENCE_RX_AND_TX_HIGH_RATE =
std::make_pair(::com::Submode::RX_AND_TX_HIGH_DATARATE, FixedArrayList<ModeListEntry, 3>());
@ -48,7 +48,7 @@ auto COM_TABLE_RX_AND_TX_HIGH_RATE_TRANS_0 =
FixedArrayList<ModeListEntry, 3>());
auto COM_TABLE_RX_AND_TX_HIGH_RATE_TRANS_1 =
std::make_pair(static_cast<uint32_t>(::com::Submode::RX_AND_TX_HIGH_DATARATE << 24) | 3,
FixedArrayList<ModeListEntry, 3>());
FixedArrayList<ModeListEntry, 6>());
auto COM_SEQUENCE_RX_AND_TX_DEFAULT_RATE =
std::make_pair(::com::Submode::RX_AND_TX_DEFAULT_DATARATE, FixedArrayList<ModeListEntry, 3>());
@ -60,7 +60,7 @@ auto COM_TABLE_RX_AND_TX_DEFAULT_RATE_TRANS_0 =
FixedArrayList<ModeListEntry, 3>());
auto COM_TABLE_RX_AND_TX_DEFAULT_RATE_TRANS_1 =
std::make_pair(static_cast<uint32_t>(::com::Submode::RX_AND_TX_DEFAULT_DATARATE << 24) | 3,
FixedArrayList<ModeListEntry, 3>());
FixedArrayList<ModeListEntry, 6>());
namespace {
@ -110,6 +110,10 @@ void buildRxOnlySequence(Subsystem& ss, ModeListEntry& eh) {
// Build RX Only transition 0
iht(objects::SYRLINKS_ASSY, NML, ::com::Submode::RX_ONLY, COM_TABLE_RX_ONLY_TRANS_0.second);
iht(objects::LOG_STORE_AND_TM_TASK, OFF, 0, COM_TABLE_RX_ONLY_TRANS_0.second);
iht(objects::HK_STORE_AND_TM_TASK, OFF, 0, COM_TABLE_RX_ONLY_TRANS_0.second);
iht(objects::CFDP_STORE_AND_TM_TASK, OFF, 0, COM_TABLE_RX_ONLY_TRANS_0.second);
iht(objects::LIVE_TM_TASK, OFF, 0, COM_TABLE_RX_ONLY_TRANS_0.second);
check(ss.addTable(TableEntry(COM_TABLE_RX_ONLY_TRANS_0.first, &COM_TABLE_RX_ONLY_TRANS_0.second)),
ctxc);
@ -165,6 +169,10 @@ void buildTxAndRxLowRateSequence(Subsystem& ss, ModeListEntry& eh) {
// Build TX and RX low transition 1
iht(objects::SYRLINKS_ASSY, NML, ::com::Submode::RX_AND_TX_LOW_DATARATE,
COM_TABLE_RX_AND_TX_LOW_RATE_TRANS_1.second);
iht(objects::LOG_STORE_AND_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_LOW_RATE_TRANS_1.second);
iht(objects::HK_STORE_AND_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_LOW_RATE_TRANS_1.second);
iht(objects::CFDP_STORE_AND_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_LOW_RATE_TRANS_1.second);
iht(objects::LIVE_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_LOW_RATE_TRANS_1.second);
check(ss.addTable(TableEntry(COM_TABLE_RX_AND_TX_LOW_RATE_TRANS_1.first,
&COM_TABLE_RX_AND_TX_LOW_RATE_TRANS_1.second)),
ctxc);
@ -217,6 +225,10 @@ void buildTxAndRxHighRateSequence(Subsystem& ss, ModeListEntry& eh) {
// Build TX and RX high transition 1
iht(objects::SYRLINKS_ASSY, NML, ::com::Submode::RX_AND_TX_HIGH_DATARATE,
COM_TABLE_RX_AND_TX_HIGH_RATE_TRANS_1.second);
iht(objects::LOG_STORE_AND_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_HIGH_RATE_TRANS_1.second);
iht(objects::HK_STORE_AND_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_HIGH_RATE_TRANS_1.second);
iht(objects::CFDP_STORE_AND_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_HIGH_RATE_TRANS_1.second);
iht(objects::LIVE_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_HIGH_RATE_TRANS_1.second);
check(ss.addTable(TableEntry(COM_TABLE_RX_AND_TX_HIGH_RATE_TRANS_1.first,
&COM_TABLE_RX_AND_TX_HIGH_RATE_TRANS_1.second)),
ctxc);
@ -271,6 +283,10 @@ void buildTxAndRxDefaultRateSequence(Subsystem& ss, ModeListEntry& eh) {
// Build TX and RX default transition 1
iht(objects::SYRLINKS_ASSY, NML, ::com::Submode::RX_AND_TX_DEFAULT_DATARATE,
COM_TABLE_RX_AND_TX_DEFAULT_RATE_TRANS_1.second);
iht(objects::LOG_STORE_AND_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_DEFAULT_RATE_TRANS_1.second);
iht(objects::HK_STORE_AND_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_DEFAULT_RATE_TRANS_1.second);
iht(objects::CFDP_STORE_AND_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_DEFAULT_RATE_TRANS_1.second);
iht(objects::LIVE_TM_TASK, ON, 0, COM_TABLE_RX_AND_TX_DEFAULT_RATE_TRANS_1.second);
check(ss.addTable(TableEntry(COM_TABLE_RX_AND_TX_DEFAULT_RATE_TRANS_1.first,
&COM_TABLE_RX_AND_TX_DEFAULT_RATE_TRANS_1.second)),
ctxc);

View File

@ -1,17 +1,11 @@
target_sources(
${LIB_EIVE_MISSION}
PRIVATE VirtualChannelWithQueue.cpp
PersistentTmStoreWithTmQueue.cpp
LiveTmTask.cpp
VirtualChannel.cpp
PRIVATE PersistentTmStoreWithTmQueue.cpp
TmFunnelHandler.cpp
TmFunnelBase.cpp
CfdpTmFunnel.cpp
tmFilters.cpp
PusLiveDemux.cpp
PersistentSingleTmStoreTask.cpp
PersistentLogTmStoreTask.cpp
TmStoreTaskBase.cpp
PusPacketFilter.cpp
PusTmRouteByFilterHelper.cpp
Service15TmStorage.cpp

View File

@ -1,27 +0,0 @@
#include "LiveTmTask.h"
#include <fsfw/tasks/TaskFactory.h>
#include <fsfw/timemanager/Stopwatch.h>
LiveTmTask::LiveTmTask(object_id_t objectId, PusTmFunnel& pusFunnel, CfdpTmFunnel& cfdpFunnel,
VirtualChannelWithQueue& channel)
: SystemObject(objectId), pusFunnel(pusFunnel), cfdpFunnel(cfdpFunnel), channel(channel) {}
ReturnValue_t LiveTmTask::performOperation(uint8_t opCode) {
while (true) {
// The funnel tasks are scheduled here directly as well.
ReturnValue_t result = channel.sendNextTm();
if (result == DirectTmSinkIF::IS_BUSY) {
sif::error << "Lost live TM, PAPB busy" << std::endl;
}
if (result == MessageQueueIF::EMPTY) {
if (tmFunnelCd.hasTimedOut()) {
pusFunnel.performOperation(0);
cfdpFunnel.performOperation(0);
tmFunnelCd.resetTimer();
}
// 40 ms IDLE delay. Might tweak this in the future.
TaskFactory::delayTask(40);
}
}
}

View File

@ -1,25 +0,0 @@
#ifndef MISSION_TMTC_LIVETMTASK_H_
#define MISSION_TMTC_LIVETMTASK_H_
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/tasks/ExecutableObjectIF.h>
#include <fsfw/timemanager/Countdown.h>
#include <mission/tmtc/CfdpTmFunnel.h>
#include <mission/tmtc/PusTmFunnel.h>
#include <mission/tmtc/VirtualChannelWithQueue.h>
class LiveTmTask : public SystemObject, public ExecutableObjectIF {
public:
LiveTmTask(object_id_t objectId, PusTmFunnel& pusFunnel, CfdpTmFunnel& cfdpFunnel,
VirtualChannelWithQueue& channel);
ReturnValue_t performOperation(uint8_t opCode) override;
private:
Countdown tmFunnelCd = Countdown(100);
PusTmFunnel& pusFunnel;
CfdpTmFunnel& cfdpFunnel;
VirtualChannelWithQueue& channel;
};
#endif /* MISSION_TMTC_LIVETMTASK_H_ */

View File

@ -251,41 +251,37 @@ ReturnValue_t PersistentTmStore::loadNextDumpFile() {
return DUMP_DONE;
}
ReturnValue_t PersistentTmStore::dumpNextPacket(DirectTmSinkIF& tmSink, size_t& dumpedLen,
bool& fileHasSwapped) {
if (state == State::IDLE) {
ReturnValue_t PersistentTmStore::getNextDumpPacket(PusTmReader& reader, bool& fileHasSwapped) {
if (state == State::IDLE or dumpParams.pendingPacketDump) {
return returnvalue::FAILED;
}
PusTmReader reader(&timeReader, fileBuf.data() + dumpParams.currentSize,
fileBuf.size() - dumpParams.currentSize);
reader.setReadOnlyData(fileBuf.data() + dumpParams.currentSize,
fileBuf.size() - dumpParams.currentSize);
// CRC check to fully ensure this is a valid TM
ReturnValue_t result = reader.parseDataWithCrcCheck();
if (result == returnvalue::OK) {
result = tmSink.write(fileBuf.data() + dumpParams.currentSize, reader.getFullPacketLen());
if (result == DirectTmSinkIF::IS_BUSY) {
return result;
} else if (result != returnvalue::OK) {
// TODO: Event?
sif::error << "PersistentTmStore: Writing to TM sink failed" << std::endl;
return result;
}
dumpParams.currentSize += reader.getFullPacketLen();
dumpedLen = reader.getFullPacketLen();
if (dumpParams.currentSize >= dumpParams.fileSize) {
fileHasSwapped = true;
return loadNextDumpFile();
}
} else {
if (result != returnvalue::OK) {
sif::error << "PersistentTmStore: Parsing of PUS TM failed with code " << result << std::endl;
triggerEvent(persTmStore::POSSIBLE_FILE_CORRUPTION, result, dumpParams.currentFileUnixStamp);
// Delete the file and load next. Could use better algorithm to partially
// restore the file dump, but for now do not trust the file.
dumpedLen = 0;
std::error_code e;
std::filesystem::remove(dumpParams.dirEntry.path().c_str(), e);
fileHasSwapped = true;
return loadNextDumpFile();
}
fileHasSwapped = false;
dumpParams.pendingPacketDump = true;
return returnvalue::OK;
}
ReturnValue_t PersistentTmStore::confirmDump(const PusTmReader& reader, bool& fileHasSwapped) {
dumpParams.pendingPacketDump = false;
dumpParams.currentSize += reader.getFullPacketLen();
if (dumpParams.currentSize >= dumpParams.fileSize) {
fileHasSwapped = true;
return loadNextDumpFile();
}
fileHasSwapped = false;
return returnvalue::OK;
}

View File

@ -4,12 +4,10 @@
#include <fsfw/ipc/CommandMessageIF.h>
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/storagemanager/StorageManagerIF.h>
#include <fsfw/timemanager/CdsShortTimeStamper.h>
#include <fsfw/tmstorage/TmStoreFrontendSimpleIF.h>
#include <fsfw/tmtcpacket/pus/tm/PusTmReader.h>
#include <fsfw/tmtcservices/AcceptsTelemetryIF.h>
#include <mission/memory/SdCardMountedIF.h>
#include <mission/tmtc/DirectTmSinkIF.h>
#include <filesystem>
@ -57,13 +55,25 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
ReturnValue_t startDumpFromUpTo(uint32_t fromUnixSeconds, uint32_t upToUnixSeconds);
/**
*
* @param tmSink
* @param dumpedLen
* @param fileHasSwapped
* @return DUMP_DONE if dump is finished, returnvalue::OK if dump of next packet was a success,
* and DirectTmSinkIF::IS_BUSY is TM sink is busy.
* @param tmReader: Next packet will be loaded into the PUS TM reader. A CRC check will be
* performed on the packet. If that check fails, the file is considered corrupted and will
* be deleted for now.
* @param fileHasSwapped: If the CRC check fails, the file will be deleted and a new one has to
* be loaded. The dump can reach completion during that process. If a file is swapped, this
* boolean is set to true
* @return DUMP_DONE if dump is finished, returnvalue::OK if the next packet was loaded into the
* TM reader, and the returnvalue of the file swap operation if the CRC check failed and
* a new file was loaded.
*/
ReturnValue_t dumpNextPacket(DirectTmSinkIF& tmSink, size_t& dumpedLen, bool& fileHasSwapped);
ReturnValue_t getNextDumpPacket(PusTmReader& tmReader, bool& fileHasSwapped);
/**
* Confirm the dump to advance the dump state machine.
* @param tmReader
* @param fileHasSwapped: If the confirmed dumps completes the current file, a new file will
* be loaded and this parameter will be set to true.
* @return If a file is swapped, the retrunvalue of the file swap operation.
*/
ReturnValue_t confirmDump(const PusTmReader& tmReader, bool& fileHasSwapped);
void getStartAndEndTimeCurrentOrLastDump(uint32_t& startTime, uint32_t& endTime) const;
ReturnValue_t storePacket(PusTmReader& reader);
@ -83,8 +93,6 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
MessageQueueIF* tcQueue;
State state = State::IDLE;
// PacketFilter filter;
CdsShortTimeStamper timeReader;
bool baseDirUninitialized = true;
const char* baseDir;
std::string baseName;
@ -96,6 +104,7 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
timeval activeFileTv{};
struct ActiveDumpParams {
bool pendingPacketDump = false;
uint32_t fromUnixTime = 0;
uint32_t untilUnixTime = 0;
uint32_t currentFileUnixStamp = 0;

View File

@ -1,131 +0,0 @@
#include "TmStoreTaskBase.h"
#include <fsfw/ipc/CommandMessageIF.h>
#include <fsfw/tasks/TaskFactory.h>
#include <fsfw/timemanager/Stopwatch.h>
#include <fsfw/tmstorage/TmStoreMessage.h>
#include "mission/persistentTmStoreDefs.h"
TmStoreTaskBase::TmStoreTaskBase(object_id_t objectId, StorageManagerIF& ipcStore,
VirtualChannel& channel, SdCardMountedIF& sdcMan)
: SystemObject(objectId), ipcStore(ipcStore), channel(channel), sdcMan(sdcMan) {}
bool TmStoreTaskBase::handleOneStore(PersistentTmStoreWithTmQueue& store,
DumpContext& dumpContext) {
ReturnValue_t result;
bool tmToStoreReceived = false;
bool tcRequestReceived = false;
bool dumpPerformed = false;
fileHasSwapped = false;
dumpContext.packetWasDumped = false;
dumpContext.vcBusyDuringDump = false;
// Store TM persistently
result = store.handleNextTm();
if (result == returnvalue::OK) {
tmToStoreReceived = true;
}
// Dump TMs when applicable
if (store.getState() == PersistentTmStore::State::DUMPING) {
if (handleOneDump(store, dumpContext, dumpPerformed) != returnvalue::OK) {
return result;
}
} else {
Command_t execCmd;
// Handle TC requests, for example deletion or retrieval requests.
result = store.handleCommandQueue(ipcStore, execCmd);
if (result == returnvalue::OK) {
if (execCmd == TmStoreMessage::DOWNLINK_STORE_CONTENT_TIME) {
cancelDumpCd.resetTimer();
tmSinkBusyCd.resetTimer();
dumpContext.reset();
}
tcRequestReceived = true;
}
}
if (tcRequestReceived or tmToStoreReceived or dumpPerformed) {
return true;
}
return false;
}
bool TmStoreTaskBase::cyclicStoreCheck() {
if (not storesInitialized) {
storesInitialized = initStoresIfPossible();
if (not storesInitialized) {
TaskFactory::delayTask(400);
return false;
}
} else if (sdCardCheckCd.hasTimedOut()) {
if (not sdcMan.isSdCardUsable(std::nullopt)) {
// Might be due to imminent shutdown or SD card switch.
storesInitialized = false;
TaskFactory::delayTask(100);
return false;
}
sdCardCheckCd.resetTimer();
}
return true;
}
void TmStoreTaskBase::cancelDump(DumpContext& ctx, PersistentTmStore& store, bool isTxOn) {
triggerEvent(ctx.eventIfCancelled, ctx.numberOfDumpedPackets, ctx.dumpedBytes);
ctx.reset();
store.cancelDump();
if (isTxOn) {
channel.cancelTransfer();
}
}
ReturnValue_t TmStoreTaskBase::handleOneDump(PersistentTmStoreWithTmQueue& store,
DumpContext& dumpContext, bool& dumpPerformed) {
ReturnValue_t result = returnvalue::OK;
// The PTME might have been reset an transmitter state change, so there is no point in continuing
// the dump.
if (not channel.isTxOn()) {
cancelDump(dumpContext, store, false);
return returnvalue::FAILED;
}
size_t dumpedLen = 0;
if (not channel.isBusy()) {
// Dump the next packet into the PTME.
dumpContext.ptmeBusyCounter = 0;
tmSinkBusyCd.resetTimer();
result = store.dumpNextPacket(channel, dumpedLen, fileHasSwapped);
if (result == DirectTmSinkIF::IS_BUSY) {
sif::warning << "Unexpected PAPB busy" << std::endl;
}
if ((result == PersistentTmStore::DUMP_DONE or result == returnvalue::OK)) {
dumpPerformed = true;
if (dumpedLen > 0) {
dumpContext.dumpedBytes += dumpedLen;
dumpContext.numberOfDumpedPackets += 1;
dumpContext.packetWasDumped = true;
}
}
if (result == PersistentTmStore::DUMP_DONE) {
uint32_t startTime;
uint32_t endTime;
store.getStartAndEndTimeCurrentOrLastDump(startTime, endTime);
triggerEvent(dumpContext.eventIfDone, dumpContext.numberOfDumpedPackets,
dumpContext.dumpedBytes);
dumpContext.reset();
}
} else {
// The PTME might be at full load, so it might sense to delay for a bit to let it do
// its work until some more bandwidth is available. Set a flag here so the upper layer can
// do ths.
dumpContext.vcBusyDuringDump = true;
dumpContext.ptmeBusyCounter++;
if (dumpContext.ptmeBusyCounter == 100) {
// If this happens, something is probably wrong.
sif::warning << "PTME busy for longer period. Cancelling dump" << std::endl;
cancelDump(dumpContext, store, channel.isTxOn());
}
}
if (cancelDumpCd.hasTimedOut() or tmSinkBusyCd.hasTimedOut()) {
cancelDump(dumpContext, store, channel.isTxOn());
}
return result;
}