Merge remote-tracking branch 'origin/develop' into thermal_controller
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good

This commit is contained in:
2023-03-31 19:18:05 +02:00
71 changed files with 1337 additions and 655 deletions

View File

@ -87,12 +87,11 @@ ReturnValue_t GyrAdis1650XHandler::scanForReply(const uint8_t *start, size_t rem
getMode() == _MODE_POWER_DOWN) {
return IGNORE_FULL_PACKET;
}
*foundLen = remainingSize;
if (remainingSize != sizeof(acs::Adis1650XReply)) {
*foundLen = remainingSize;
return returnvalue::FAILED;
}
*foundId = adis1650x::REPLY;
*foundLen = remainingSize;
if (internalState == InternalState::SHUTDOWN) {
commandExecuted = true;
}

View File

@ -99,12 +99,11 @@ ReturnValue_t GyrL3gCustomHandler::scanForReply(const uint8_t *start, size_t len
if (getMode() == _MODE_WAIT_OFF or getMode() == _MODE_WAIT_ON or getMode() == _MODE_POWER_DOWN) {
return IGNORE_FULL_PACKET;
}
*foundLen = len;
if (len != sizeof(acs::GyroL3gReply)) {
*foundLen = len;
return returnvalue::FAILED;
}
*foundId = l3gd20h::REPLY;
*foundLen = len;
*foundId = adis1650x::REPLY;
if (internalState == InternalState::SHUTDOWN) {
commandExecuted = true;
}

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

@ -0,0 +1,74 @@
#include "PersistentLogTmStoreTask.h"
#include <fsfw/tasks/TaskFactory.h>
#include <fsfw/timemanager/Stopwatch.h>
PersistentLogTmStoreTask::PersistentLogTmStoreTask(object_id_t objectId, StorageManagerIF& ipcStore,
LogStores stores, VirtualChannel& channel,
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),
miscStoreContext(persTmStore::DUMP_MISC_STORE_DONE, persTmStore::DUMP_MISC_CANCELLED) {}
ReturnValue_t PersistentLogTmStoreTask::performOperation(uint8_t opCode) {
bool someonesBusy = false;
bool vcBusyDuringDump = false;
auto stateHandlingForStore = [&](bool storeIsBusy, DumpContext& ctx) {
if (storeIsBusy) {
someonesBusy = true;
}
if (fileHasSwapped) {
someFileWasSwapped = fileHasSwapped;
}
if (ctx.vcBusyDuringDump) {
vcBusyDuringDump = true;
}
};
while (true) {
readCommandQueue();
if (not cyclicStoreCheck()) {
continue;
}
someonesBusy = false;
someFileWasSwapped = false;
vcBusyDuringDump = false;
stateHandlingForStore(handleOneStore(stores.okStore, okStoreContext), okStoreContext);
stateHandlingForStore(handleOneStore(stores.notOkStore, notOkStoreContext), notOkStoreContext);
stateHandlingForStore(handleOneStore(stores.miscStore, miscStoreContext), miscStoreContext);
if (not someonesBusy) {
TaskFactory::delayTask(100);
} else if (vcBusyDuringDump) {
// TODO: Might not be necessary
sif::debug << "VC busy, delaying" << std::endl;
TaskFactory::delayTask(10);
}
}
}
bool PersistentLogTmStoreTask::initStoresIfPossible() {
if (sdcMan.isSdCardUsable(std::nullopt)) {
stores.okStore.initializeTmStore();
stores.miscStore.initializeTmStore();
stores.notOkStore.initializeTmStore();
return true;
}
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;
@ -35,7 +36,8 @@ class PersistentLogTmStoreTask : public TmStoreTaskBase, public ExecutableObject
Countdown graceDelayDuringDumping = Countdown(200);
bool someFileWasSwapped = false;
bool initStoresIfPossible();
bool initStoresIfPossible() override;
void startTransition(Mode_t mode, Submode_t submode) override;
};
#endif /* MISSION_TMTC_PERSISTENTLOGTMSTORETASK_H_ */

View File

@ -0,0 +1,54 @@
#include "PersistentSingleTmStoreTask.h"
#include <fsfw/tasks/TaskFactory.h>
#include <fsfw/timemanager/Stopwatch.h>
#include <unistd.h>
PersistentSingleTmStoreTask::PersistentSingleTmStoreTask(
object_id_t objectId, StorageManagerIF& ipcStore, PersistentTmStoreWithTmQueue& tmStore,
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;
}
bool busy = handleOneStore(storeWithQueue, dumpContext);
if (not busy) {
TaskFactory::delayTask(100);
} else if (dumpContext.vcBusyDuringDump) {
sif::debug << "VC busy, delaying" << std::endl;
// 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);
}
}
}
bool PersistentSingleTmStoreTask::initStoresIfPossible() {
if (sdcMan.isSdCardUsable(std::nullopt)) {
storeWithQueue.initializeTmStore();
return true;
}
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,15 +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, SdCardMountedIF& sdcMan);
Event eventIfDumpDone, Event eventIfCancelled,
SdCardMountedIF& sdcMan, const std::atomic_bool& ptmeLocked);
ReturnValue_t performOperation(uint8_t opCode) override;
@ -21,7 +22,9 @@ class PersistentSingleTmStoreTask : public TmStoreTaskBase, public ExecutableObj
Countdown tcHandlingCd = Countdown(400);
Countdown graceDelayDuringDumping = Countdown(100);
bool initStoresIfPossible();
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

@ -0,0 +1,106 @@
#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>
/**
* 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,
public HasModesIF,
public ModeTreeChildIF,
public ModeTreeConnectionIF {
public:
struct DumpContext {
DumpContext(Event eventIfDone, Event eventIfCancelled)
: eventIfDone(eventIfDone), eventIfCancelled(eventIfCancelled) {}
void reset() {
numberOfDumpedPackets = 0;
dumpedBytes = 0;
vcBusyDuringDump = false;
packetWasDumped = false;
bytesDumpedAtLastDelay = 0;
ptmeBusyCounter = 0;
}
const Event eventIfDone;
const Event eventIfCancelled;
size_t numberOfDumpedPackets = 0;
size_t bytesDumpedAtLastDelay = 0;
size_t dumpedBytes = 0;
uint32_t ptmeBusyCounter = 0;
bool packetWasDumped = false;
bool vcBusyDuringDump = false;
};
TmStoreTaskBase(object_id_t objectId, StorageManagerIF& ipcStore, VirtualChannel& channel,
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);
bool storesInitialized = false;
bool fileHasSwapped = false;
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);
/**
*
* Handling for one store. Returns if anything was done.
* @param store
* @return
*/
bool handleOneStore(PersistentTmStoreWithTmQueue& store, DumpContext& dumpContext);
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
* be used again and re-initialize stores. Returns whether store is okay to be used.
*/
bool cyclicStoreCheck();
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,12 +1,8 @@
#include "VirtualChannel.h"
VirtualChannel::VirtualChannel(object_id_t objectId, uint8_t vcId, const char* vcName, PtmeIF& ptme,
const std::atomic_bool& linkStateProvider)
: SystemObject(objectId),
ptme(ptme),
vcId(vcId),
vcName(vcName),
linkStateProvider(linkStateProvider) {}
const std::atomic_bool& txOn)
: SystemObject(objectId), ptme(ptme), vcId(vcId), vcName(vcName), txOn(txOn) {}
ReturnValue_t VirtualChannel::initialize() { return returnvalue::OK; }
@ -15,7 +11,7 @@ ReturnValue_t VirtualChannel::sendNextTm(const uint8_t* data, size_t size) {
}
ReturnValue_t VirtualChannel::write(const uint8_t* data, size_t size) {
if (linkStateProvider.load()) {
if (txOn) {
return ptme.writeToVc(vcId, data, size);
}
return returnvalue::OK;
@ -27,10 +23,12 @@ const char* VirtualChannel::getName() const { return vcName.c_str(); }
bool VirtualChannel::isBusy() const {
// Data is discarded, so channel is not busy.
if (linkStateProvider.load()) {
if (not txOn) {
return false;
}
return ptme.isBusy(vcId);
}
void VirtualChannel::cancelTransfer() { ptme.cancelTransfer(vcId); }
bool VirtualChannel::isTxOn() const { return txOn; }

View File

@ -30,6 +30,7 @@ class VirtualChannel : public SystemObject, public VirtualChannelIF {
ReturnValue_t write(const uint8_t* data, size_t size) override;
void cancelTransfer() override;
uint8_t getVcid() const;
bool isTxOn() const;
const char* getName() const;
@ -37,5 +38,5 @@ class VirtualChannel : public SystemObject, public VirtualChannelIF {
PtmeIF& ptme;
uint8_t vcId = 0;
std::string vcName;
const std::atomic_bool& linkStateProvider;
const std::atomic_bool& txOn;
};

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,15 +36,13 @@ ReturnValue_t VirtualChannelWithQueue::sendNextTm() {
return result;
}
result = write(data, size);
if (result != returnvalue::OK) {
return result;
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);
if (result != returnvalue::OK) {
return result;
}
return returnvalue::OK;
return result;
}
MessageQueueId_t VirtualChannelWithQueue::getReportReceptionQueue(uint8_t virtualChannel) const {

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

@ -347,7 +347,8 @@ void ObjectFactory::createAcsBoardAssy(PowerSwitchIF& pwrSwitcher,
AcsBoardHelper acsBoardHelper = AcsBoardHelper(
objects::MGM_0_LIS3_HANDLER, objects::MGM_1_RM3100_HANDLER, objects::MGM_2_LIS3_HANDLER,
objects::MGM_3_RM3100_HANDLER, objects::GYRO_0_ADIS_HANDLER, objects::GYRO_1_L3G_HANDLER,
objects::GYRO_2_ADIS_HANDLER, objects::GYRO_3_L3G_HANDLER, objects::GPS_CONTROLLER);
objects::GYRO_2_ADIS_HANDLER, objects::GYRO_3_L3G_HANDLER, objects::GPS_CONTROLLER,
objects::GPS_0_HEALTH_DEV, objects::GPS_1_HEALTH_DEV);
auto acsAss =
new AcsBoardAssembly(objects::ACS_BOARD_ASS, &pwrSwitcher, acsBoardHelper, gpioComIF);
for (auto& assChild : assemblyDhbs) {

View File

@ -205,7 +205,7 @@ ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, cons
using namespace scex;
ReturnValue_t status = OK;
auto oneFileHandler = [&](std::string cmdName) {
auto oneFileHandler = [&](const char* cmdName) {
auto activeSd = sdcMan.getActiveSdCard();
if (not activeSd) {
return HasFileSystemIF::FILESYSTEM_INACTIVE;
@ -216,7 +216,7 @@ ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, cons
if (prefix == nullptr) {
return returnvalue::FAILED;
}
oss << prefix << "/scex/scex-" << cmdName << fileId << ".bin";
oss << prefix << "/scex/scex-" << cmdName << "-" << fileId << ".bin";
fileName = oss.str();
ofstream out(fileName, ofstream::binary);
if (out.bad()) {
@ -227,7 +227,7 @@ ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, cons
out << helper;
return OK;
};
auto multiFileHandler = [&](std::string cmdName) {
auto multiFileHandler = [&](const char* cmdName) {
if ((helper.getPacketCounter() == 1) or (not fileNameSet)) {
auto activeSd = sdcMan.getActiveSdCard();
if (not activeSd) {
@ -264,31 +264,31 @@ ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, cons
id = helper.getCmd();
switch (id) {
case (PING): {
status = oneFileHandler("ping_");
status = oneFileHandler(PING_IDLE_BASE_NAME);
break;
}
case (ION_CMD): {
status = oneFileHandler("ion_");
status = oneFileHandler(ION_BASE_NAME);
break;
}
case (TEMP_CMD): {
status = oneFileHandler("temp_");
status = oneFileHandler(TEMPERATURE_BASE_NAME);
break;
}
case (EXP_STATUS_CMD): {
status = oneFileHandler("exp_status_");
status = oneFileHandler(EXP_STATUS_BASE_NAME);
break;
}
case (FRAM): {
status = multiFileHandler("fram_");
status = multiFileHandler(FRAM_BASE_NAME);
break;
}
case (ONE_CELL): {
status = multiFileHandler("one_cell_");
status = multiFileHandler(ONE_CELL_BASE_NAME);
break;
}
case (ALL_CELLS_CMD): {
status = multiFileHandler("multi_cell_");
status = multiFileHandler(ALL_CELLS_BASE_NAME);
break;
}
default:
@ -362,9 +362,9 @@ std::string ScexDeviceHandler::date_time_string() {
ostringstream oss(std::ostringstream::ate);
if (tod.hour < 10) {
oss << tod.year << tod.month << tod.day << "_0" << tod.hour;
oss << tod.year << tod.month << tod.day << "T0" << tod.hour;
} else {
oss << tod.year << tod.month << tod.day << "_" << tod.hour;
oss << tod.year << tod.month << tod.day << "T" << tod.hour;
}
if (tod.minute < 10) {
oss << 0 << tod.minute;

View File

@ -13,6 +13,14 @@ class SdCardMountedIF;
class ScexDeviceHandler : public DeviceHandlerBase {
public:
static constexpr char FRAM_BASE_NAME[] = "framContent";
static constexpr char ION_BASE_NAME[] = "ion";
static constexpr char TEMPERATURE_BASE_NAME[] = "temperature";
static constexpr char EXP_STATUS_BASE_NAME[] = "expStatus";
static constexpr char ONE_CELL_BASE_NAME[] = "oneCell";
static constexpr char ALL_CELLS_BASE_NAME[] = "allCells";
static constexpr char PING_IDLE_BASE_NAME[] = "pingIdle";
ScexDeviceHandler(object_id_t objectId, ScexUartReader &reader, CookieIF *cookie,
SdCardMountedIF &sdcMan);
void setPowerSwitcher(PowerSwitchIF &powerSwitcher, power::Switch_t switchId);

View File

@ -25,8 +25,6 @@ static constexpr Event POSSIBLE_FILE_CORRUPTION = event::makeEvent(SUBSYSTEM_ID,
//! P2: Allowed file size
static constexpr Event FILE_TOO_LARGE = event::makeEvent(SUBSYSTEM_ID, 1, severity::LOW);
static constexpr Event BUSY_DUMPING_EVENT = event::makeEvent(SUBSYSTEM_ID, 2, severity::INFO);
//! [EXPORT] : [COMMENT] Dump was cancelled. P1: Object ID of store.
static constexpr Event DUMP_WAS_CANCELLED = event::makeEvent(SUBSYSTEM_ID, 3, severity::LOW);
//! [EXPORT] : [COMMENT] P1: Number of dumped packets. P2: Total dumped bytes.
static constexpr Event DUMP_OK_STORE_DONE = event::makeEvent(SUBSYSTEM_ID, 5, severity::INFO);
@ -38,6 +36,17 @@ static constexpr Event DUMP_MISC_STORE_DONE = event::makeEvent(SUBSYSTEM_ID, 7,
static constexpr Event DUMP_HK_STORE_DONE = event::makeEvent(SUBSYSTEM_ID, 8, severity::INFO);
//! [EXPORT] : [COMMENT] P1: Number of dumped packets. P2: Total dumped bytes.
static constexpr Event DUMP_CFDP_STORE_DONE = event::makeEvent(SUBSYSTEM_ID, 9, severity::INFO);
//! [EXPORT] : [COMMENT] P1: Number of dumped packets. P2: Total dumped bytes.
static constexpr Event DUMP_OK_CANCELLED = event::makeEvent(SUBSYSTEM_ID, 10, severity::LOW);
//! [EXPORT] : [COMMENT] P1: Number of dumped packets. P2: Total dumped bytes.
static constexpr Event DUMP_NOK_CANCELLED = event::makeEvent(SUBSYSTEM_ID, 11, severity::LOW);
//! [EXPORT] : [COMMENT] P1: Number of dumped packets. P2: Total dumped bytes.
static constexpr Event DUMP_MISC_CANCELLED = event::makeEvent(SUBSYSTEM_ID, 12, severity::LOW);
//! [EXPORT] : [COMMENT] P1: Number of dumped packets. P2: Total dumped bytes.
static constexpr Event DUMP_HK_CANCELLED = event::makeEvent(SUBSYSTEM_ID, 13, severity::LOW);
//! [EXPORT] : [COMMENT] P1: Number of dumped packets. P2: Total dumped bytes.
static constexpr Event DUMP_CFDP_CANCELLED = event::makeEvent(SUBSYSTEM_ID, 14, severity::LOW);
}; // namespace persTmStore
#endif /* MISSION_PERSISTENTTMSTOREDEFS_H_ */

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

@ -77,14 +77,16 @@ ReturnValue_t AcsBoardAssembly::checkChildrenStateOn(Mode_t wantedMode, Submode_
if (wantedSubmode == A_SIDE) {
if ((helper.gyro0SideAMode != wantedMode and helper.gyro1SideAMode != wantedMode) or
(helper.mgm0SideAMode != wantedMode and helper.mgm1SideAMode != wantedMode) or
helper.gpsMode != MODE_ON) {
(helper.gpsMode != MODE_ON) or
(healthHelper.healthTable->getHealth(helper.healthDevGps0) == FAULTY)) {
return NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE;
}
return returnvalue::OK;
} else if (wantedSubmode == B_SIDE) {
if ((helper.gyro2SideBMode != wantedMode and helper.gyro3SideBMode != wantedMode) or
(helper.mgm2SideBMode != wantedMode and helper.mgm3SideBMode != wantedMode) or
helper.gpsMode != MODE_ON) {
(helper.gpsMode != MODE_ON) or
(healthHelper.healthTable->getHealth(helper.healthDevGps1) == FAULTY)) {
return NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE;
}
return returnvalue::OK;
@ -126,12 +128,33 @@ ReturnValue_t AcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t s
auto cmdSeq = [&](object_id_t objectId, Mode_t devMode, ModeTableIdx tableIdx) {
if (mode == devMode) {
modeTable[tableIdx].setMode(mode);
} else if (isUseable(objectId, devMode)) {
} else if (isModeCommandable(objectId, devMode)) {
modeTable[tableIdx].setMode(mode);
modeTable[tableIdx].setSubmode(SUBMODE_NONE);
}
};
bool gpsUsable = isUseable(helper.gpsId, helper.gpsMode);
bool gpsUsable = isModeCommandable(helper.gpsId, helper.gpsMode);
auto gpsCmd = [&](bool gnss0NReset, bool gnss1NReset, uint8_t gnssSelect) {
if (gpsUsable) {
if (mode == MODE_ON or mode == DeviceHandlerIF::MODE_NORMAL) {
modeTable[ModeTableIdx::GPS].setMode(MODE_ON);
} else if (mode == MODE_OFF) {
gnss0NReset = true;
gnss1NReset = true;
modeTable[ModeTableIdx::GPS].setMode(MODE_OFF);
}
modeTable[ModeTableIdx::GPS].setSubmode(SUBMODE_NONE);
gpioHandler(gpioIds::GNSS_0_NRESET, gnss0NReset,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 0");
gpioHandler(gpioIds::GNSS_1_NRESET, gnss1NReset,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 1");
gpioHandler(gpioIds::GNSS_SELECT, gnssSelect,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull GNSS select");
}
};
switch (submode) {
case (A_SIDE): {
modeTable[ModeTableIdx::GYRO_2_B].setMode(MODE_OFF);
@ -146,16 +169,7 @@ ReturnValue_t AcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t s
cmdSeq(helper.gyro1L3gIdSideA, helper.gyro1SideAMode, ModeTableIdx::GYRO_1_A);
cmdSeq(helper.mgm0Lis3IdSideA, helper.mgm0SideAMode, ModeTableIdx::MGM_0_A);
cmdSeq(helper.mgm1Rm3100IdSideA, helper.mgm1SideAMode, ModeTableIdx::MGM_1_A);
if (gpsUsable) {
gpioHandler(gpioIds::GNSS_0_NRESET, true,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 0 high (used GNSS)");
gpioHandler(gpioIds::GNSS_1_NRESET, false,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 1 low (unused GNSS)");
gpioHandler(gpioIds::GNSS_SELECT, false,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull GNSS select low");
}
gpsCmd(true, false, 0);
break;
}
case (B_SIDE): {
@ -171,20 +185,10 @@ ReturnValue_t AcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t s
cmdSeq(helper.gyro3L3gIdSideB, helper.gyro3SideBMode, ModeTableIdx::GYRO_3_B);
cmdSeq(helper.mgm2Lis3IdSideB, helper.mgm2SideBMode, ModeTableIdx::MGM_2_B);
cmdSeq(helper.mgm3Rm3100IdSideB, helper.mgm3SideBMode, ModeTableIdx::MGM_3_B);
if (gpsUsable) {
gpioHandler(gpioIds::GNSS_0_NRESET, false,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 0 low (unused GNSS)");
gpioHandler(gpioIds::GNSS_1_NRESET, true,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 1 high (used GNSS)");
gpioHandler(gpioIds::GNSS_SELECT, true,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull GNSS select high");
}
gpsCmd(false, true, 1);
break;
}
case (DUAL_MODE): {
cmdSeq(helper.gpsId, helper.gpsMode, ModeTableIdx::GPS);
cmdSeq(helper.gyro0AdisIdSideA, helper.gyro0SideAMode, ModeTableIdx::GYRO_0_A);
cmdSeq(helper.gyro1L3gIdSideA, helper.gyro1SideAMode, ModeTableIdx::GYRO_1_A);
cmdSeq(helper.mgm0Lis3IdSideA, helper.mgm0SideAMode, ModeTableIdx::MGM_0_A);
@ -193,26 +197,10 @@ ReturnValue_t AcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t s
cmdSeq(helper.gyro3L3gIdSideB, helper.gyro3SideBMode, ModeTableIdx::GYRO_3_B);
cmdSeq(helper.mgm2Lis3IdSideB, helper.mgm2SideBMode, ModeTableIdx::MGM_2_B);
cmdSeq(helper.mgm3Rm3100IdSideB, helper.mgm3SideBMode, ModeTableIdx::MGM_3_B);
ReturnValue_t status = returnvalue::OK;
if (gpsUsable) {
gpioHandler(gpioIds::GNSS_0_NRESET, true,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 0 high (used GNSS)");
gpioHandler(gpioIds::GNSS_1_NRESET, true,
"AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull nReset pin"
"of GNSS 1 high (used GNSS)");
if (defaultSubmode == Submodes::A_SIDE) {
status = gpioIF->pullLow(gpioIds::GNSS_SELECT);
} else {
status = gpioIF->pullHigh(gpioIds::GNSS_SELECT);
}
if (status != returnvalue::OK) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::error << "AcsBoardAssembly::handleNormalOrOnModeCmd: Could not pull GNSS select to"
"default side for dual mode"
<< std::endl;
#endif
}
if (defaultSubmode == Submodes::A_SIDE) {
gpsCmd(true, true, 0);
} else {
gpsCmd(true, true, 1);
}
break;
}
@ -220,10 +208,6 @@ ReturnValue_t AcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t s
sif::error << "AcsBoardAssembly::handleNormalModeCmd: Unknown submode" << std::endl;
}
}
if (gpsUsable) {
modeTable[ModeTableIdx::GPS].setMode(MODE_ON);
modeTable[ModeTableIdx::GPS].setSubmode(SUBMODE_NONE);
}
if (needsSecondStep) {
result = NEED_SECOND_STEP;
}
@ -289,25 +273,54 @@ ReturnValue_t AcsBoardAssembly::checkAndHandleHealthStates(Mode_t deviceMode,
Submode_t deviceSubmode) {
using namespace returnvalue;
ReturnValue_t status = returnvalue::OK;
auto overwriteHealthForOneDev = [&](object_id_t dev) {
HealthState health = healthHelper.healthTable->getHealth(dev);
if (health == FAULTY or health == PERMANENT_FAULTY) {
overwriteDeviceHealth(dev, health);
status = NEED_TO_CHANGE_HEALTH;
} else if (health == EXTERNAL_CONTROL) {
auto checkAcsBoardSensorGroup = [&](object_id_t o0, object_id_t o1, object_id_t o2,
object_id_t o3) {
HealthState h0 = healthHelper.healthTable->getHealth(o0);
HealthState h1 = healthHelper.healthTable->getHealth(o1);
HealthState h2 = healthHelper.healthTable->getHealth(o2);
HealthState h3 = healthHelper.healthTable->getHealth(o3);
if ((h0 == FAULTY or h0 == PERMANENT_FAULTY) and (h1 == FAULTY or h1 == PERMANENT_FAULTY) and
(h2 == FAULTY or h2 == PERMANENT_FAULTY) and (h3 == FAULTY or h3 == PERMANENT_FAULTY)) {
overwriteDeviceHealth(o0, h0);
overwriteDeviceHealth(o1, h1);
overwriteDeviceHealth(o2, h2);
overwriteDeviceHealth(o3, h3);
}
if (h0 == EXTERNAL_CONTROL or h1 == EXTERNAL_CONTROL or h2 == EXTERNAL_CONTROL or
h3 == EXTERNAL_CONTROL) {
modeHelper.setForced(true);
}
};
if (healthHelper.healthTable->getHealth(helper.gpsId) == EXTERNAL_CONTROL) {
modeHelper.setForced(true);
}
if (deviceSubmode == duallane::DUAL_MODE) {
overwriteHealthForOneDev(helper.mgm0Lis3IdSideA);
overwriteHealthForOneDev(helper.mgm1Rm3100IdSideA);
overwriteHealthForOneDev(helper.mgm2Lis3IdSideB);
overwriteHealthForOneDev(helper.mgm3Rm3100IdSideB);
overwriteHealthForOneDev(helper.gyro0AdisIdSideA);
overwriteHealthForOneDev(helper.gyro1L3gIdSideA);
overwriteHealthForOneDev(helper.gyro2AdisIdSideB);
overwriteHealthForOneDev(helper.gyro3L3gIdSideB);
overwriteHealthForOneDev(helper.gpsId);
checkAcsBoardSensorGroup(helper.mgm0Lis3IdSideA, helper.mgm1Rm3100IdSideA,
helper.mgm2Lis3IdSideB, helper.mgm3Rm3100IdSideB);
checkAcsBoardSensorGroup(helper.gyro0AdisIdSideA, helper.gyro1L3gIdSideA,
helper.gyro2AdisIdSideB, helper.gyro3L3gIdSideB);
}
return status;
}
void AcsBoardAssembly::handleChildrenLostMode(ReturnValue_t result) {
using namespace duallane;
// Special handling to account for GPS devices being faulty. If the GPS device on the other
// side is marked faulty, directly to to dual side.
if (submode == Submodes::A_SIDE) {
if (healthHelper.healthTable->getHealth(helper.healthDevGps1) == FAULTY or
healthHelper.healthTable->getHealth(helper.healthDevGps1) == PERMANENT_FAULTY) {
triggerEvent(DIRECT_TRANSITION_TO_DUAL_OTHER_GPS_FAULTY, submode, 0);
startTransition(mode, Submodes::DUAL_MODE);
return;
}
} else if (submode == Submodes::B_SIDE) {
if (healthHelper.healthTable->getHealth(helper.healthDevGps0) == FAULTY or
healthHelper.healthTable->getHealth(helper.healthDevGps0) == PERMANENT_FAULTY) {
triggerEvent(DIRECT_TRANSITION_TO_DUAL_OTHER_GPS_FAULTY, submode, 0);
startTransition(mode, Submodes::DUAL_MODE);
return;
}
}
DualLaneAssemblyBase::handleChildrenLostMode(result);
}

View File

@ -12,7 +12,7 @@
struct AcsBoardHelper {
AcsBoardHelper(object_id_t mgm0Id, object_id_t mgm1Id, object_id_t mgm2Id, object_id_t mgm3Id,
object_id_t gyro0Id, object_id_t gyro1Id, object_id_t gyro2Id, object_id_t gyro3Id,
object_id_t gpsId)
object_id_t gpsId, object_id_t gps0HealthDev, object_id_t gps1HealthDev)
: mgm0Lis3IdSideA(mgm0Id),
mgm1Rm3100IdSideA(mgm1Id),
mgm2Lis3IdSideB(mgm2Id),
@ -35,6 +35,9 @@ struct AcsBoardHelper {
object_id_t gpsId = objects::NO_OBJECT;
object_id_t healthDevGps0 = objects::NO_OBJECT;
object_id_t healthDevGps1 = objects::NO_OBJECT;
Mode_t gyro0SideAMode = HasModesIF::MODE_OFF;
Mode_t gyro1SideAMode = HasModesIF::MODE_OFF;
Mode_t gyro2SideBMode = HasModesIF::MODE_OFF;
@ -91,6 +94,11 @@ class AcsBoardAssembly : public DualLaneAssemblyBase {
//! desired mode/submode combination
static constexpr Event SIDE_SWITCH_TRANSITION_NOT_ALLOWED =
event::makeEvent(SUBSYSTEM_ID, 3, severity::LOW);
//! [EXPORT] : [COMMENT] This is triggered when the assembly would have normally switched
//! the board side, but the GPS device of the other side was marked faulty.
//! P1: Current submode.
static constexpr Event DIRECT_TRANSITION_TO_DUAL_OTHER_GPS_FAULTY =
event::makeEvent(SUBSYSTEM_ID, 4, severity::MEDIUM);
static constexpr uint8_t NUMBER_DEVICES_MODE_TABLE = 9;
@ -120,6 +128,8 @@ class AcsBoardAssembly : public DualLaneAssemblyBase {
ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override;
ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override;
void handleChildrenLostMode(ReturnValue_t result) override;
ReturnValue_t handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode);
ReturnValue_t checkAndHandleHealthStates(Mode_t deviceMode, Submode_t deviceSubmode);
void refreshHelperModes();

View File

@ -2,6 +2,7 @@ target_sources(
${LIB_EIVE_MISSION}
PRIVATE AcsBoardAssembly.cpp
AcsSubsystem.cpp
StrAssembly.cpp
DualLaneAssemblyBase.cpp
ImtqAssembly.cpp
RwAssembly.cpp

View File

@ -46,6 +46,9 @@ void DualLaneAssemblyBase::startTransition(Mode_t mode, Submode_t submode) {
AssemblyBase::startTransition(mode, submode);
return;
}
if (sideSwitchState == SideSwitchState::NONE and sideSwitchTransition(mode, submode)) {
sideSwitchState = SideSwitchState::REQUESTED;
}
uint8_t pwrSubmode = submode;
if (sideSwitchState == SideSwitchState::REQUESTED) {
pwrSubmode = duallane::DUAL_MODE;
@ -61,7 +64,7 @@ void DualLaneAssemblyBase::startTransition(Mode_t mode, Submode_t submode) {
}
}
bool DualLaneAssemblyBase::isUseable(object_id_t object, Mode_t mode) {
bool DualLaneAssemblyBase::isModeCommandable(object_id_t object, Mode_t mode) {
if (healthHelper.healthTable->isFaulty(object)) {
return false;
}
@ -70,10 +73,7 @@ bool DualLaneAssemblyBase::isUseable(object_id_t object, Mode_t mode) {
if (childrenMap[object].mode == mode) {
return true;
}
if (healthHelper.healthTable->isCommandable(object)) {
return true;
}
// Check for external control health state is done by base class.
return false;
}
@ -115,9 +115,6 @@ ReturnValue_t DualLaneAssemblyBase::isModeCombinationValid(Mode_t mode, Submode_
if (submode != A_SIDE and submode != B_SIDE and submode != DUAL_MODE) {
return returnvalue::FAILED;
}
if (sideSwitchTransition(mode, submode)) {
sideSwitchState = SideSwitchState::REQUESTED;
}
return returnvalue::OK;
}

View File

@ -49,12 +49,12 @@ class DualLaneAssemblyBase : public AssemblyBase, public ConfirmsFailuresIF {
MessageQueueIF* eventQueue = nullptr;
/**
* Check whether it makes sense to send mode commands to the device
* Check whether it makes sense to send mode commands to the device.
* @param object
* @param mode
* @return
*/
bool isUseable(object_id_t object, Mode_t mode);
bool isModeCommandable(object_id_t object, Mode_t mode);
/**
* Thin wrapper function which is required because the helper class

View File

@ -158,18 +158,29 @@ void SusAssembly::refreshHelperModes() {
ReturnValue_t SusAssembly::checkAndHandleHealthStates(Mode_t deviceMode, Submode_t deviceSubmode) {
using namespace returnvalue;
ReturnValue_t status = returnvalue::OK;
auto overwriteHealthForOneDev = [&](object_id_t dev) {
auto checkSusGroup = [&](object_id_t devNom, object_id_t devRed) {
HealthState healthNom = healthHelper.healthTable->getHealth(devNom);
HealthState healthRed = healthHelper.healthTable->getHealth(devRed);
if ((healthNom == FAULTY or healthNom == PERMANENT_FAULTY) and
(healthRed == FAULTY or healthRed == PERMANENT_FAULTY)) {
overwriteDeviceHealth(devNom, healthNom);
overwriteDeviceHealth(devRed, healthRed);
}
};
auto checkHealthForOneDev = [&](object_id_t dev) {
HealthState health = healthHelper.healthTable->getHealth(dev);
if (health == FAULTY or health == PERMANENT_FAULTY) {
overwriteDeviceHealth(dev, health);
status = NEED_TO_CHANGE_HEALTH;
} else if (health == EXTERNAL_CONTROL) {
if (health == EXTERNAL_CONTROL) {
modeHelper.setForced(true);
}
};
if (deviceSubmode == duallane::DUAL_MODE) {
for (uint8_t idx = 0; idx < 12; idx++) {
overwriteHealthForOneDev(helper.susIds[idx]);
uint8_t idx = 0;
for (idx = 0; idx < 6; idx++) {
checkSusGroup(helper.susIds[idx], helper.susIds[idx + 6]);
checkHealthForOneDev(helper.susIds[idx]);
}
for (idx = 6; idx < 12; idx++) {
checkHealthForOneDev(helper.susIds[idx]);
}
}
return status;

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

@ -5,6 +5,5 @@ target_sources(
TcsSubsystem.cpp
PayloadSubsystem.cpp
Stack5VHandler.cpp
StrAssembly.cpp
PowerStateMachineBase.cpp
TcsBoardAssembly.cpp)

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

@ -1,50 +0,0 @@
#include "PersistentLogTmStoreTask.h"
#include <fsfw/tasks/TaskFactory.h>
#include <fsfw/timemanager/Stopwatch.h>
PersistentLogTmStoreTask::PersistentLogTmStoreTask(object_id_t objectId, StorageManagerIF& ipcStore,
LogStores stores, VirtualChannel& channel,
SdCardMountedIF& sdcMan)
: TmStoreTaskBase(objectId, ipcStore, channel, sdcMan),
stores(stores),
okStoreContext(persTmStore::DUMP_OK_STORE_DONE),
notOkStoreContext(persTmStore::DUMP_NOK_STORE_DONE),
miscStoreContext(persTmStore::DUMP_MISC_STORE_DONE) {}
ReturnValue_t PersistentLogTmStoreTask::performOperation(uint8_t opCode) {
bool someonesBusy = false;
auto stateHandlingForStore = [&](bool storeIsBusy) {
if (storeIsBusy) {
someonesBusy = true;
}
if (fileHasSwapped) {
someFileWasSwapped = fileHasSwapped;
}
};
while (true) {
if (not cyclicStoreCheck()) {
continue;
}
someonesBusy = false;
someFileWasSwapped = false;
stateHandlingForStore(handleOneStore(stores.okStore, okStoreContext));
stateHandlingForStore(handleOneStore(stores.notOkStore, notOkStoreContext));
stateHandlingForStore(handleOneStore(stores.miscStore, miscStoreContext));
if (not someonesBusy) {
TaskFactory::delayTask(100);
} else if (someFileWasSwapped) {
TaskFactory::delayTask(10);
}
}
}
bool PersistentLogTmStoreTask::initStoresIfPossible() {
if (sdcMan.isSdCardUsable(std::nullopt)) {
stores.okStore.initializeTmStore();
stores.miscStore.initializeTmStore();
stores.notOkStore.initializeTmStore();
return true;
}
return false;
}

View File

@ -1,33 +0,0 @@
#include <fsfw/tasks/TaskFactory.h>
#include <fsfw/timemanager/Stopwatch.h>
#include <mission/tmtc/PersistentSingleTmStoreTask.h>
PersistentSingleTmStoreTask::PersistentSingleTmStoreTask(
object_id_t objectId, StorageManagerIF& ipcStore, PersistentTmStoreWithTmQueue& tmStore,
VirtualChannel& channel, Event eventIfDumpDone, SdCardMountedIF& sdcMan)
: TmStoreTaskBase(objectId, ipcStore, channel, sdcMan),
storeWithQueue(tmStore),
dumpContext(eventIfDumpDone) {}
ReturnValue_t PersistentSingleTmStoreTask::performOperation(uint8_t opCode) {
while (true) {
// Delay done by the check
if (not cyclicStoreCheck()) {
continue;
}
bool busy = handleOneStore(storeWithQueue, dumpContext);
if (not busy) {
TaskFactory::delayTask(100);
} else if (fileHasSwapped) {
TaskFactory::delayTask(10);
}
}
}
bool PersistentSingleTmStoreTask::initStoresIfPossible() {
if (sdcMan.isSdCardUsable(std::nullopt)) {
storeWithQueue.initializeTmStore();
return true;
}
return false;
}

View File

@ -171,7 +171,7 @@ void PersistentTmStore::deleteUpTo(uint32_t unixSeconds) {
// Convert file time to the UNIX epoch
struct tm fileTime {};
if (pathToTime(file.path(), fileTime) != returnvalue::OK) {
sif::error << "Time extraction for " << file << "failed" << std::endl;
sif::error << "Time extraction for " << file << " failed" << std::endl;
continue;
}
time_t fileEpoch = timegm(&fileTime);
@ -212,7 +212,7 @@ ReturnValue_t PersistentTmStore::loadNextDumpFile() {
sif::error << "PersistentTmStore: Could not retrieve file size: " << e.message() << std::endl;
continue;
}
sif::debug << "Path: " << dumpParams.dirEntry.path() << std::endl;
// sif::debug << "Path: " << dumpParams.dirEntry.path() << std::endl;
// File empty or can't even read CCSDS header.
if (dumpParams.fileSize <= 6) {
@ -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,96 +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 dumpsPerformed = false;
// Store TM persistently
result = store.handleNextTm();
if (result == returnvalue::OK) {
tmToStoreReceived = true;
}
// Dump TMs when applicable
if (store.getState() == PersistentTmStore::State::DUMPING) {
size_t dumpedLen = 0;
if (not channel.isBusy()) {
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) and dumpedLen > 0) {
dumpContext.dumpedBytes += dumpedLen;
dumpContext.numberOfDumpedPackets += 1;
}
if (result == PersistentTmStore::DUMP_DONE) {
uint32_t startTime;
uint32_t endTime;
store.getStartAndEndTimeCurrentOrLastDump(startTime, endTime);
triggerEvent(dumpContext.eventIfDone, dumpContext.numberOfDumpedPackets,
dumpContext.dumpedBytes);
dumpsPerformed = true;
} else if (result == returnvalue::OK) {
dumpsPerformed = true;
}
} else {
dumpContext.ptmeBusyCounter++;
if (dumpContext.ptmeBusyCounter == 50) {
sif::warning << "PTME busy for longer period. Dumped length so far: "
<< dumpContext.dumpedBytes << std::endl;
dumpContext.ptmeBusyCounter = 0;
}
}
if (cancelDumpCd.hasTimedOut() or tmSinkBusyCd.hasTimedOut()) {
triggerEvent(persTmStore::DUMP_WAS_CANCELLED, store.getObjectId());
store.cancelDump();
}
} 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 dumpsPerformed) {
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;
}

View File

@ -1,53 +0,0 @@
#ifndef MISSION_TMTC_TMSTORETASKBASE_H_
#define MISSION_TMTC_TMSTORETASKBASE_H_
#include <fsfw/timemanager/Countdown.h>
#include <mission/tmtc/PersistentTmStoreWithTmQueue.h>
#include <mission/tmtc/VirtualChannel.h>
class TmStoreTaskBase : public SystemObject {
public:
struct DumpContext {
DumpContext(Event eventIfDone) : eventIfDone(eventIfDone) {}
void reset() {
numberOfDumpedPackets = 0;
dumpedBytes = 0;
}
const Event eventIfDone;
uint32_t numberOfDumpedPackets = 0;
uint32_t dumpedBytes = 0;
uint32_t ptmeBusyCounter = 0;
};
TmStoreTaskBase(object_id_t objectId, StorageManagerIF& ipcStore, VirtualChannel& channel,
SdCardMountedIF& sdcMan);
protected:
/**
* Handling for one store. Returns if anything was done.
* @param store
* @return
*/
bool handleOneStore(PersistentTmStoreWithTmQueue& store, DumpContext& dumpContext);
/**
* Occasionally check whether SD card is okay to be used. If not, poll whether it is ready to
* be used again and re-initialize stores. Returns whether store is okay to be used.
*/
bool cyclicStoreCheck();
virtual bool initStoresIfPossible() = 0;
StorageManagerIF& ipcStore;
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;
};
#endif /* MISSION_TMTC_TMSTORETASKBASE_H_ */