#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. // TODO: Not really clean here.. would be better if the executed command is returns as an // enumeration. result = store.handleCommandQueue(ipcStore, execCmd); if (execCmd == TmStoreMessage::DOWNLINK_STORE_CONTENT_TIME) { if (result == PersistentTmStore::DUMP_DONE) { dumpDoneHandler(store, dumpContext); } else if (result == returnvalue::OK) { cancelDumpCd.resetTimer(); tmSinkBusyCd.resetTimer(); dumpContext.reset(); } } if (execCmd != CommandMessageIF::CMD_NONE) { 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; // 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; // Dump the next packet into the PTME. dumpContext.ptmeBusyCounter = 0; tmSinkBusyCd.resetTimer(); ReturnValue_t result = store.getNextDumpPacket(tmReader, fileHasSwapped); if (fileHasSwapped and result == PersistentTmStore::DUMP_DONE) { // This can happen if a file is corrupted and the next file swap completes the dump. dumpDoneHandler(store, dumpContext); return returnvalue::OK; } else if (result != returnvalue::OK) { sif::error << "PersistentTmStore: Getting next dump packet failed" << std::endl; return result; } dumpedLen = tmReader.getFullPacketLen(); size_t partiallyWrittenSize = 0; result = channel.write(tmReader.getFullData(), dumpedLen, partiallyWrittenSize); if (result == VirtualChannelIF::PARTIALLY_WRITTEN) { result = channel.handleLastWriteSynchronously(tmReader.getFullData(), partiallyWrittenSize, dumpedLen - partiallyWrittenSize, 20); if (result != returnvalue::OK) { // TODO: Event? Might lead to dangerous spam though.. sif::warning << "PersistentTmStore: Synchronous write of last segment failed with code 0x" << std::setw(4) << std::hex << result << std::endl; } } else 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(store, dumpContext); } 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); } void TmStoreTaskBase::dumpDoneHandler(PersistentTmStore& store, DumpContext& dumpContext) { uint32_t startTime; uint32_t endTime; store.getStartAndEndTimeCurrentOrLastDump(startTime, endTime); triggerEvent(dumpContext.eventIfDone, dumpContext.numberOfDumpedPackets, dumpContext.dumpedBytes); dumpContext.reset(); } 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; } }