diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d982765..b69ce243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index e0ec4dd7..3a2842a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}) diff --git a/bsp_hosted/ObjectFactory.cpp b/bsp_hosted/ObjectFactory.cpp index b49bbf61..77ea7828 100644 --- a/bsp_hosted/ObjectFactory.cpp +++ b/bsp_hosted/ObjectFactory.cpp @@ -29,7 +29,7 @@ #include #include -#include "dummies/helpers.h" +#include "dummies/helperFactory.h" #ifdef PLATFORM_UNIX #include diff --git a/bsp_q7s/core/ObjectFactory.cpp b/bsp_q7s/core/ObjectFactory.cpp index e7a5fca0..c1b8ea4e 100644 --- a/bsp_q7s/core/ObjectFactory.cpp +++ b/bsp_q7s/core/ObjectFactory.cpp @@ -20,15 +20,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include #include -#include -#include -#include #include "OBSWConfig.h" #include "bsp_q7s/boardtest/Q7STestTask.h" @@ -81,6 +81,7 @@ using gpio::Levels; #include #include #include +#include #include #include #include @@ -96,7 +97,6 @@ using gpio::Levels; #include #include #include -#include #include @@ -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) { diff --git a/bsp_q7s/core/ObjectFactory.h b/bsp_q7s/core/ObjectFactory.h index dc65e1f4..b7380f2a 100644 --- a/bsp_q7s/core/ObjectFactory.h +++ b/bsp_q7s/core/ObjectFactory.h @@ -4,11 +4,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include @@ -24,6 +24,7 @@ class AcsBoardAssembly; class GpioIF; extern std::atomic_uint16_t I2C_FATAL_ERRORS; +extern std::atomic_bool PTME_LOCKED; namespace ObjectFactory { diff --git a/bsp_q7s/em/emObjectFactory.cpp b/bsp_q7s/em/emObjectFactory.cpp index c0852c05..1f873b99 100644 --- a/bsp_q7s/em/emObjectFactory.cpp +++ b/bsp_q7s/em/emObjectFactory.cpp @@ -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" diff --git a/dummies/CMakeLists.txt b/dummies/CMakeLists.txt index 75dd1364..6a49fcc6 100644 --- a/dummies/CMakeLists.txt +++ b/dummies/CMakeLists.txt @@ -26,6 +26,6 @@ target_sources( CoreControllerDummy.cpp PlocMpsocDummy.cpp PlocSupervisorDummy.cpp - helpers.cpp + helperFactory.cpp MgmRm3100Dummy.cpp Tmp1075Dummy.cpp) diff --git a/dummies/helpers.cpp b/dummies/helperFactory.cpp similarity index 98% rename from dummies/helpers.cpp rename to dummies/helperFactory.cpp index 1a541dfd..c0390a5e 100644 --- a/dummies/helpers.cpp +++ b/dummies/helperFactory.cpp @@ -1,4 +1,4 @@ -#include "helpers.h" +#include "helperFactory.h" #include #include @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -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); } diff --git a/dummies/helpers.h b/dummies/helperFactory.h similarity index 100% rename from dummies/helpers.h rename to dummies/helperFactory.h diff --git a/linux/ipcore/PapbVcInterface.cpp b/linux/ipcore/PapbVcInterface.cpp index eacfce33..f00475c8 100644 --- a/linux/ipcore/PapbVcInterface.cpp +++ b/linux/ipcore/PapbVcInterface.cpp @@ -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(&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(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(); } diff --git a/linux/ipcore/PapbVcInterface.h b/linux/ipcore/PapbVcInterface.h index b4454dae..42cfbc19 100644 --- a/linux/ipcore/PapbVcInterface.h +++ b/linux/ipcore/PapbVcInterface.h @@ -5,6 +5,8 @@ #include #include +#include + #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 diff --git a/mission/com/CMakeLists.txt b/mission/com/CMakeLists.txt index 94d3e7f0..90e90c88 100644 --- a/mission/com/CMakeLists.txt +++ b/mission/com/CMakeLists.txt @@ -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) diff --git a/mission/com/CcsdsIpCoreHandler.cpp b/mission/com/CcsdsIpCoreHandler.cpp index 806096b8..d260d50e 100644 --- a/mission/com/CcsdsIpCoreHandler.cpp +++ b/mission/com/CcsdsIpCoreHandler.cpp @@ -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(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(com::CcsdsSubmode::DATARATE_LOW); - result = ptmeConfig.setRate(RATE_100KBPS); - break; - } - case SET_HIGH_RATE: { - submode = static_cast(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(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(com::CcsdsSubmode::DATARATE_HIGH)) { - rateSet(RATE_500KBPS); - } else if (submode == static_cast(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(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(com::CcsdsSubmode::DATARATE_HIGH)) { + result = ptmeConfig.setRate(RATE_500KBPS); + } else if (submode == static_cast(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; + } +} diff --git a/mission/com/CcsdsIpCoreHandler.h b/mission/com/CcsdsIpCoreHandler.h index 0fad0aae..785f84a5 100644 --- a/mission/com/CcsdsIpCoreHandler.h +++ b/mission/com/CcsdsIpCoreHandler.h @@ -2,7 +2,7 @@ #define CCSDSHANDLER_H_ #include -#include +#include #include #include @@ -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; - // 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_ */ diff --git a/mission/com/LiveTmTask.cpp b/mission/com/LiveTmTask.cpp new file mode 100644 index 00000000..d09c6ced --- /dev/null +++ b/mission/com/LiveTmTask.cpp @@ -0,0 +1,107 @@ +#include "LiveTmTask.h" + +#include +#include +#include +#include + +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; +} diff --git a/mission/com/LiveTmTask.h b/mission/com/LiveTmTask.h new file mode 100644 index 00000000..c203f1de --- /dev/null +++ b/mission/com/LiveTmTask.h @@ -0,0 +1,57 @@ +#ifndef MISSION_TMTC_LIVETMTASK_H_ +#define MISSION_TMTC_LIVETMTASK_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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_ */ diff --git a/mission/tmtc/PersistentLogTmStoreTask.cpp b/mission/com/PersistentLogTmStoreTask.cpp similarity index 74% rename from mission/tmtc/PersistentLogTmStoreTask.cpp rename to mission/com/PersistentLogTmStoreTask.cpp index dcd78b54..8ba7200f 100644 --- a/mission/tmtc/PersistentLogTmStoreTask.cpp +++ b/mission/com/PersistentLogTmStoreTask.cpp @@ -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); +} diff --git a/mission/tmtc/PersistentLogTmStoreTask.h b/mission/com/PersistentLogTmStoreTask.h similarity index 85% rename from mission/tmtc/PersistentLogTmStoreTask.h rename to mission/com/PersistentLogTmStoreTask.h index 116b369e..18da93f5 100644 --- a/mission/tmtc/PersistentLogTmStoreTask.h +++ b/mission/com/PersistentLogTmStoreTask.h @@ -5,11 +5,11 @@ #include #include #include +#include +#include #include #include #include -#include -#include 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_ */ diff --git a/mission/tmtc/PersistentSingleTmStoreTask.cpp b/mission/com/PersistentSingleTmStoreTask.cpp similarity index 59% rename from mission/tmtc/PersistentSingleTmStoreTask.cpp rename to mission/com/PersistentSingleTmStoreTask.cpp index a2d57208..10bedee5 100644 --- a/mission/tmtc/PersistentSingleTmStoreTask.cpp +++ b/mission/com/PersistentSingleTmStoreTask.cpp @@ -1,17 +1,21 @@ +#include "PersistentSingleTmStoreTask.h" + #include #include -#include #include 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); +} diff --git a/mission/tmtc/PersistentSingleTmStoreTask.h b/mission/com/PersistentSingleTmStoreTask.h similarity index 79% rename from mission/tmtc/PersistentSingleTmStoreTask.h rename to mission/com/PersistentSingleTmStoreTask.h index 1529928f..1a4c8683 100644 --- a/mission/tmtc/PersistentSingleTmStoreTask.h +++ b/mission/com/PersistentSingleTmStoreTask.h @@ -3,16 +3,16 @@ #include #include +#include +#include #include -#include -#include 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_ */ diff --git a/mission/com/SyrlinksHandler.cpp b/mission/com/SyrlinksHandler.cpp index 5065f219..297ffd88 100644 --- a/mission/com/SyrlinksHandler.cpp +++ b/mission/com/SyrlinksHandler.cpp @@ -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; } diff --git a/mission/com/SyrlinksHandler.h b/mission/com/SyrlinksHandler.h index 7d2d7227..97541ecb 100644 --- a/mission/com/SyrlinksHandler.h +++ b/mission/com/SyrlinksHandler.h @@ -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]; diff --git a/mission/com/TmStoreTaskBase.cpp b/mission/com/TmStoreTaskBase.cpp new file mode 100644 index 00000000..6598225d --- /dev/null +++ b/mission/com/TmStoreTaskBase.cpp @@ -0,0 +1,228 @@ +#include "TmStoreTaskBase.h" + +#include +#include +#include +#include +#include +#include + +#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; + } +} diff --git a/mission/tmtc/TmStoreTaskBase.h b/mission/com/TmStoreTaskBase.h similarity index 59% rename from mission/tmtc/TmStoreTaskBase.h rename to mission/com/TmStoreTaskBase.h index 5fef74ff..ef61bd19 100644 --- a/mission/tmtc/TmStoreTaskBase.h +++ b/mission/com/TmStoreTaskBase.h @@ -1,15 +1,22 @@ #ifndef MISSION_TMTC_TMSTORETASKBASE_H_ #define MISSION_TMTC_TMSTORETASKBASE_H_ +#include +#include +#include +#include #include +#include #include -#include /** * 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_ */ diff --git a/mission/tmtc/VirtualChannel.cpp b/mission/com/VirtualChannel.cpp similarity index 100% rename from mission/tmtc/VirtualChannel.cpp rename to mission/com/VirtualChannel.cpp diff --git a/mission/tmtc/VirtualChannel.h b/mission/com/VirtualChannel.h similarity index 100% rename from mission/tmtc/VirtualChannel.h rename to mission/com/VirtualChannel.h diff --git a/mission/tmtc/VirtualChannelWithQueue.cpp b/mission/com/VirtualChannelWithQueue.cpp similarity index 91% rename from mission/tmtc/VirtualChannelWithQueue.cpp rename to mission/com/VirtualChannelWithQueue.cpp index 44a20031..a90829ab 100644 --- a/mission/tmtc/VirtualChannelWithQueue.cpp +++ b/mission/com/VirtualChannelWithQueue.cpp @@ -1,4 +1,4 @@ -#include +#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); diff --git a/mission/tmtc/VirtualChannelWithQueue.h b/mission/com/VirtualChannelWithQueue.h similarity index 93% rename from mission/tmtc/VirtualChannelWithQueue.h rename to mission/com/VirtualChannelWithQueue.h index 0c060a06..04c7f965 100644 --- a/mission/tmtc/VirtualChannelWithQueue.h +++ b/mission/com/VirtualChannelWithQueue.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include @@ -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; diff --git a/mission/config/comCfg.cpp b/mission/config/comCfg.cpp index 8387214d..664a2e55 100644 --- a/mission/config/comCfg.cpp +++ b/mission/config/comCfg.cpp @@ -3,6 +3,8 @@ #include #include +#include + com::Datarate DATARATE_CFG_RAW = com::Datarate::LOW_RATE_MODULATION_BPSK; MutexIF* DATARATE_LOCK = nullptr; diff --git a/mission/pollingSeqTables.cpp b/mission/pollingSeqTables.cpp index 8d172132..4225e977 100644 --- a/mission/pollingSeqTables.cpp +++ b/mission/pollingSeqTables.cpp @@ -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(length); diff --git a/mission/system/com/comModeTree.cpp b/mission/system/com/comModeTree.cpp index 9501262d..bb9a8d10 100644 --- a/mission/system/com/comModeTree.cpp +++ b/mission/system/com/comModeTree.cpp @@ -22,7 +22,7 @@ auto COM_SEQUENCE_RX_ONLY = auto COM_TABLE_RX_ONLY_TGT = std::make_pair( static_cast(::com::Submode::RX_ONLY << 24) | 1, FixedArrayList()); auto COM_TABLE_RX_ONLY_TRANS_0 = std::make_pair( - static_cast(::com::Submode::RX_ONLY << 24) | 2, FixedArrayList()); + static_cast(::com::Submode::RX_ONLY << 24) | 2, FixedArrayList()); auto COM_TABLE_RX_ONLY_TRANS_1 = std::make_pair( static_cast(::com::Submode::RX_ONLY << 24) | 3, FixedArrayList()); @@ -36,7 +36,7 @@ auto COM_TABLE_RX_AND_TX_LOW_RATE_TRANS_0 = FixedArrayList()); auto COM_TABLE_RX_AND_TX_LOW_RATE_TRANS_1 = std::make_pair(static_cast(::com::Submode::RX_AND_TX_LOW_DATARATE << 24) | 3, - FixedArrayList()); + FixedArrayList()); auto COM_SEQUENCE_RX_AND_TX_HIGH_RATE = std::make_pair(::com::Submode::RX_AND_TX_HIGH_DATARATE, FixedArrayList()); @@ -48,7 +48,7 @@ auto COM_TABLE_RX_AND_TX_HIGH_RATE_TRANS_0 = FixedArrayList()); auto COM_TABLE_RX_AND_TX_HIGH_RATE_TRANS_1 = std::make_pair(static_cast(::com::Submode::RX_AND_TX_HIGH_DATARATE << 24) | 3, - FixedArrayList()); + FixedArrayList()); auto COM_SEQUENCE_RX_AND_TX_DEFAULT_RATE = std::make_pair(::com::Submode::RX_AND_TX_DEFAULT_DATARATE, FixedArrayList()); @@ -60,7 +60,7 @@ auto COM_TABLE_RX_AND_TX_DEFAULT_RATE_TRANS_0 = FixedArrayList()); auto COM_TABLE_RX_AND_TX_DEFAULT_RATE_TRANS_1 = std::make_pair(static_cast(::com::Submode::RX_AND_TX_DEFAULT_DATARATE << 24) | 3, - FixedArrayList()); + FixedArrayList()); 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); diff --git a/mission/tmtc/CMakeLists.txt b/mission/tmtc/CMakeLists.txt index c797e315..a8388d83 100644 --- a/mission/tmtc/CMakeLists.txt +++ b/mission/tmtc/CMakeLists.txt @@ -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 diff --git a/mission/tmtc/LiveTmTask.cpp b/mission/tmtc/LiveTmTask.cpp deleted file mode 100644 index 7e6d3de6..00000000 --- a/mission/tmtc/LiveTmTask.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "LiveTmTask.h" - -#include -#include - -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); - } - } -} diff --git a/mission/tmtc/LiveTmTask.h b/mission/tmtc/LiveTmTask.h deleted file mode 100644 index a0ca6b83..00000000 --- a/mission/tmtc/LiveTmTask.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef MISSION_TMTC_LIVETMTASK_H_ -#define MISSION_TMTC_LIVETMTASK_H_ - -#include -#include -#include -#include -#include -#include - -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_ */ diff --git a/mission/tmtc/PersistentTmStore.cpp b/mission/tmtc/PersistentTmStore.cpp index bf8f2a4f..a5f0ec66 100644 --- a/mission/tmtc/PersistentTmStore.cpp +++ b/mission/tmtc/PersistentTmStore.cpp @@ -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; } diff --git a/mission/tmtc/PersistentTmStore.h b/mission/tmtc/PersistentTmStore.h index a0910026..4839f2fb 100644 --- a/mission/tmtc/PersistentTmStore.h +++ b/mission/tmtc/PersistentTmStore.h @@ -4,12 +4,10 @@ #include #include #include -#include #include #include #include #include -#include #include @@ -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; diff --git a/mission/tmtc/TmStoreTaskBase.cpp b/mission/tmtc/TmStoreTaskBase.cpp deleted file mode 100644 index 7ca03488..00000000 --- a/mission/tmtc/TmStoreTaskBase.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "TmStoreTaskBase.h" - -#include -#include -#include -#include - -#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; -}