diff --git a/.idea/cmake.xml b/.idea/cmake.xml
new file mode 100644
index 000000000..b0a4921a9
--- /dev/null
+++ b/.idea/cmake.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 693e59e7c..9f5bb9bbc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,8 +10,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Fixes
+- The `PusTmCreator` API only accepted 255 bytes of source data. It can now accept source
+ data with a size limited only by the size of `size_t`.
+- Important bugfix in CFDP PDU header format: The entity length field and the transaction sequence
+ number fields stored the actual length of the field instead of the length minus 1 like specified
+ in the CFDP standard.
- PUS Health Service: Size check for set health command.
- Perform operation completion for announce health command.
+ Perform operation completion for announce health command.
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/746
- Linux OSAL `getUptime` fix: Check validity of `/proc/uptime` file before reading uptime.
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/745
@@ -22,8 +27,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- add CFDP subsystem ID
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/742
+- `PusTmZcWriter` now exposes API to set message counter field.
## Changed
+
+- HK generation is now countdown based.
- Bump ETL version to 20.35.14
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/748
- Renamed `PCDU_2` subsystem ID to `POWER_SWITCH_IF`.
@@ -32,6 +40,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/743
- Assert that `FixedArrayList` is larger than 0 at compile time.
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/740
+- Health functions are virtual now.
# [v6.0.0] 2023-02-10
@@ -107,6 +116,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Added
+- `CServiceHealthCommanding`: Add announce all health info implementation
+ PR: https://egit.irs.uni-stuttgart.de/eive/fsfw/pulls/122
- Empty constructor for `CdsShortTimeStamper` which does not do an object manager registration.
PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/730
- `Service9TimeManagement`: Add `DUMP_TIME` (129) subservice.
diff --git a/docs/conf.py b/docs/conf.py
index a4232026b..dad85e61b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,7 +51,10 @@ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
html_theme = "alabaster"
html_theme_options = {
- "extra_nav_links": {"Impressum" : "https://www.uni-stuttgart.de/impressum", "Datenschutz": "https://info.irs.uni-stuttgart.de/datenschutz/datenschutzWebmit.html"}
+ "extra_nav_links": {
+ "Impressum": "https://www.uni-stuttgart.de/impressum",
+ "Datenschutz": "https://info.irs.uni-stuttgart.de/datenschutz/datenschutzWebmit.html",
+ }
}
diff --git a/src/fsfw/action/ActionHelper.cpp b/src/fsfw/action/ActionHelper.cpp
index fd6c8afba..81a1a727c 100644
--- a/src/fsfw/action/ActionHelper.cpp
+++ b/src/fsfw/action/ActionHelper.cpp
@@ -59,17 +59,24 @@ void ActionHelper::setQueueToUse(MessageQueueIF* queue) { queueToUse = queue; }
void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId,
store_address_t dataAddress) {
+ bool hasAdditionalData = false;
const uint8_t* dataPtr = nullptr;
size_t size = 0;
- ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size);
- if (result != returnvalue::OK) {
- CommandMessage reply;
- ActionMessage::setStepReply(&reply, actionId, 0, result);
- queueToUse->sendMessage(commandedBy, &reply);
- return;
+ ReturnValue_t result;
+ if (dataAddress != store_address_t::invalid()) {
+ hasAdditionalData = true;
+ ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size);
+ if (result != returnvalue::OK) {
+ CommandMessage reply;
+ ActionMessage::setStepReply(&reply, actionId, 0, result);
+ queueToUse->sendMessage(commandedBy, &reply);
+ return;
+ }
}
result = owner->executeAction(actionId, commandedBy, dataPtr, size);
- ipcStore->deleteData(dataAddress);
+ if (hasAdditionalData) {
+ ipcStore->deleteData(dataAddress);
+ }
if (result == HasActionsIF::EXECUTION_FINISHED) {
CommandMessage reply;
ActionMessage::setCompletionReply(&reply, actionId, true, result);
diff --git a/src/fsfw/action/CommandActionHelper.h b/src/fsfw/action/CommandActionHelper.h
index 55176e09b..ac8ed1ba8 100644
--- a/src/fsfw/action/CommandActionHelper.h
+++ b/src/fsfw/action/CommandActionHelper.h
@@ -16,8 +16,8 @@ class CommandActionHelper {
public:
explicit CommandActionHelper(CommandsActionsIF* owner);
virtual ~CommandActionHelper();
- ReturnValue_t commandAction(object_id_t commandTo, ActionId_t actionId, const uint8_t* data,
- uint32_t size);
+ ReturnValue_t commandAction(object_id_t commandTo, ActionId_t actionId,
+ const uint8_t* data = nullptr, uint32_t size = 0);
ReturnValue_t commandAction(object_id_t commandTo, ActionId_t actionId, SerializeIF* data);
ReturnValue_t initialize();
ReturnValue_t handleReply(CommandMessage* reply);
diff --git a/src/fsfw/cfdp.h b/src/fsfw/cfdp.h
index 86086432d..b26459783 100644
--- a/src/fsfw/cfdp.h
+++ b/src/fsfw/cfdp.h
@@ -2,7 +2,9 @@
#define FSFW_CFDP_H
#include "cfdp/definitions.h"
+#include "cfdp/handler/DestHandler.h"
#include "cfdp/handler/FaultHandlerBase.h"
+#include "cfdp/helpers.h"
#include "cfdp/tlv/Lv.h"
#include "cfdp/tlv/StringLv.h"
#include "cfdp/tlv/Tlv.h"
diff --git a/src/fsfw/cfdp/handler/CMakeLists.txt b/src/fsfw/cfdp/handler/CMakeLists.txt
index 901308060..7ad995c03 100644
--- a/src/fsfw/cfdp/handler/CMakeLists.txt
+++ b/src/fsfw/cfdp/handler/CMakeLists.txt
@@ -1 +1,2 @@
-target_sources(${LIB_FSFW_NAME} PRIVATE FaultHandlerBase.cpp UserBase.cpp)
+target_sources(${LIB_FSFW_NAME} PRIVATE SourceHandler.cpp DestHandler.cpp
+ FaultHandlerBase.cpp UserBase.cpp)
diff --git a/src/fsfw/cfdp/handler/DestHandler.cpp b/src/fsfw/cfdp/handler/DestHandler.cpp
new file mode 100644
index 000000000..4ffa797ff
--- /dev/null
+++ b/src/fsfw/cfdp/handler/DestHandler.cpp
@@ -0,0 +1,546 @@
+#include "DestHandler.h"
+
+#include
+
+#include
+
+#include "fsfw/FSFW.h"
+#include "fsfw/cfdp/pdu/EofPduReader.h"
+#include "fsfw/cfdp/pdu/FileDataReader.h"
+#include "fsfw/cfdp/pdu/FinishedPduCreator.h"
+#include "fsfw/cfdp/pdu/PduHeaderReader.h"
+#include "fsfw/objectmanager.h"
+#include "fsfw/tmtcservices/TmTcMessage.h"
+
+using namespace returnvalue;
+
+cfdp::DestHandler::DestHandler(DestHandlerParams params, FsfwParams fsfwParams)
+ : tlvVec(params.maxTlvsInOnePdu),
+ userTlvVec(params.maxTlvsInOnePdu),
+ dp(std::move(params)),
+ fp(fsfwParams),
+ tp(params.maxFilenameLen) {
+ tp.pduConf.direction = cfdp::Direction::TOWARDS_SENDER;
+}
+
+const cfdp::DestHandler::FsmResult& cfdp::DestHandler::performStateMachine() {
+ ReturnValue_t result;
+ uint8_t errorIdx = 0;
+ fsmRes.resetOfIteration();
+ if (fsmRes.step == TransactionStep::IDLE) {
+ for (auto infoIter = dp.packetListRef.begin(); infoIter != dp.packetListRef.end();) {
+ if (infoIter->pduType == PduType::FILE_DIRECTIVE and
+ infoIter->directiveType == FileDirective::METADATA) {
+ result = handleMetadataPdu(*infoIter);
+ checkAndHandleError(result, errorIdx);
+ // Store data was deleted in PDU handler because a store guard is used
+ dp.packetListRef.erase(infoIter++);
+ } else {
+ infoIter++;
+ }
+ }
+ if (fsmRes.step == TransactionStep::IDLE) {
+ // To decrease the already high complexity of the software, all packets arriving before
+ // a metadata PDU are deleted.
+ for (auto infoIter = dp.packetListRef.begin(); infoIter != dp.packetListRef.end();) {
+ fp.tcStore->deleteData(infoIter->storeId);
+ infoIter++;
+ }
+ dp.packetListRef.clear();
+ }
+
+ if (fsmRes.step != TransactionStep::IDLE) {
+ fsmRes.callStatus = CallStatus::CALL_AGAIN;
+ }
+ return updateFsmRes(errorIdx);
+ }
+ if (fsmRes.state == CfdpStates::BUSY_CLASS_1_NACKED) {
+ if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
+ for (auto infoIter = dp.packetListRef.begin(); infoIter != dp.packetListRef.end();) {
+ if (infoIter->pduType == PduType::FILE_DATA) {
+ result = handleFileDataPdu(*infoIter);
+ checkAndHandleError(result, errorIdx);
+ // Store data was deleted in PDU handler because a store guard is used
+ dp.packetListRef.erase(infoIter++);
+ } else if (infoIter->pduType == PduType::FILE_DIRECTIVE and
+ infoIter->directiveType == FileDirective::EOF_DIRECTIVE) {
+ // TODO: Support for check timer missing
+ result = handleEofPdu(*infoIter);
+ checkAndHandleError(result, errorIdx);
+ // Store data was deleted in PDU handler because a store guard is used
+ dp.packetListRef.erase(infoIter++);
+ } else {
+ infoIter++;
+ }
+ }
+ }
+ if (fsmRes.step == TransactionStep::TRANSFER_COMPLETION) {
+ result = handleTransferCompletion();
+ checkAndHandleError(result, errorIdx);
+ }
+ if (fsmRes.step == TransactionStep::SENDING_FINISHED_PDU) {
+ result = sendFinishedPdu();
+ checkAndHandleError(result, errorIdx);
+ finish();
+ }
+ return updateFsmRes(errorIdx);
+ }
+ if (fsmRes.state == CfdpStates::BUSY_CLASS_2_ACKED) {
+ // TODO: Will be implemented at a later stage
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::warning << "CFDP state machine for acknowledged mode not implemented yet" << std::endl;
+#endif
+ }
+ return updateFsmRes(errorIdx);
+}
+
+ReturnValue_t cfdp::DestHandler::passPacket(PacketInfo packet) {
+ if (dp.packetListRef.full()) {
+ return FAILED;
+ }
+ dp.packetListRef.push_back(packet);
+ return OK;
+}
+
+ReturnValue_t cfdp::DestHandler::initialize() {
+ if (fp.tmStore == nullptr) {
+ fp.tmStore = ObjectManager::instance()->get(objects::TM_STORE);
+ if (fp.tmStore == nullptr) {
+ return FAILED;
+ }
+ }
+
+ if (fp.tcStore == nullptr) {
+ fp.tcStore = ObjectManager::instance()->get(objects::TC_STORE);
+ if (fp.tcStore == nullptr) {
+ return FAILED;
+ }
+ }
+
+ if (fp.msgQueue == nullptr) {
+ return FAILED;
+ }
+ return OK;
+}
+
+ReturnValue_t cfdp::DestHandler::handleMetadataPdu(const PacketInfo& info) {
+ // Process metadata PDU
+ auto constAccessorPair = fp.tcStore->getData(info.storeId);
+ if (constAccessorPair.first != OK) {
+ // TODO: This is not a CFDP error. Event and/or warning?
+ return constAccessorPair.first;
+ }
+ cfdp::StringLv sourceFileName;
+ cfdp::StringLv destFileName;
+ MetadataInfo metadataInfo(tp.fileSize, sourceFileName, destFileName);
+ cfdp::Tlv* tlvArrayAsPtr = tlvVec.data();
+ metadataInfo.setOptionsArray(&tlvArrayAsPtr, std::nullopt, tlvVec.size());
+ MetadataPduReader reader(constAccessorPair.second.data(), constAccessorPair.second.size(),
+ metadataInfo);
+ ReturnValue_t result = reader.parseData();
+ // TODO: The standard does not really specify what happens if this kind of error happens
+ // I think it might be a good idea to cache some sort of error code, which
+ // is translated into a warning and/or event by an upper layer
+ if (result != OK) {
+ return handleMetadataParseError(result, constAccessorPair.second.data(),
+ constAccessorPair.second.size());
+ }
+ return startTransaction(reader, metadataInfo);
+}
+
+ReturnValue_t cfdp::DestHandler::handleFileDataPdu(const cfdp::PacketInfo& info) {
+ // Process file data PDU
+ auto constAccessorPair = fp.tcStore->getData(info.storeId);
+ if (constAccessorPair.first != OK) {
+ // TODO: This is not a CFDP error. Event and/or warning?
+ return constAccessorPair.first;
+ }
+ cfdp::FileSize offset;
+ FileDataInfo fdInfo(offset);
+ FileDataReader reader(constAccessorPair.second.data(), constAccessorPair.second.size(), fdInfo);
+ ReturnValue_t result = reader.parseData();
+ if (result != OK) {
+ return result;
+ }
+ size_t fileSegmentLen = 0;
+ const uint8_t* fileData = fdInfo.getFileData(&fileSegmentLen);
+ FileOpParams fileOpParams(tp.destName.data(), fileSegmentLen);
+ fileOpParams.offset = offset.value();
+ if (dp.cfg.indicCfg.fileSegmentRecvIndicRequired) {
+ FileSegmentRecvdParams segParams;
+ segParams.offset = offset.value();
+ segParams.id = tp.transactionId;
+ segParams.length = fileSegmentLen;
+ segParams.recContState = fdInfo.getRecordContinuationState();
+ size_t segmentMetadatLen = 0;
+ auto* segMetadata = fdInfo.getSegmentMetadata(&segmentMetadatLen);
+ segParams.segmentMetadata = {segMetadata, segmentMetadatLen};
+ dp.user.fileSegmentRecvdIndication(segParams);
+ }
+ result = dp.user.vfs.writeToFile(fileOpParams, fileData);
+ if (result != returnvalue::OK) {
+ // TODO: Proper Error handling
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::error << "cfdp::DestHandler: VFS file write error with code 0x" << std::hex << std::setw(2)
+ << result << std::endl;
+#endif
+ tp.vfsErrorCount++;
+ if (tp.vfsErrorCount < 3) {
+ // TODO: Provide execution step as parameter
+ fp.eventReporter->forwardEvent(events::FILESTORE_ERROR, static_cast(fsmRes.step),
+ result);
+ }
+ return result;
+ } else {
+ tp.deliveryStatus = FileDeliveryStatus::RETAINED_IN_FILESTORE;
+ tp.vfsErrorCount = 0;
+ }
+ if (offset.value() + fileSegmentLen > tp.progress) {
+ tp.progress = offset.value() + fileSegmentLen;
+ }
+ return result;
+}
+
+ReturnValue_t cfdp::DestHandler::handleEofPdu(const cfdp::PacketInfo& info) {
+ // Process EOF PDU
+ auto constAccessorPair = fp.tcStore->getData(info.storeId);
+ if (constAccessorPair.first != OK) {
+ // TODO: This is not a CFDP error. Event and/or warning?
+ return constAccessorPair.first;
+ }
+ EofInfo eofInfo(nullptr);
+ EofPduReader reader(constAccessorPair.second.data(), constAccessorPair.second.size(), eofInfo);
+ ReturnValue_t result = reader.parseData();
+ if (result != OK) {
+ return result;
+ }
+ // TODO: Error handling
+ if (eofInfo.getConditionCode() == ConditionCode::NO_ERROR) {
+ tp.crc = eofInfo.getChecksum();
+ uint64_t fileSizeFromEof = eofInfo.getFileSize().value();
+ // CFDP 4.6.1.2.9: Declare file size error if progress exceeds file size
+ if (fileSizeFromEof > tp.progress) {
+ // TODO: File size error
+ }
+ tp.fileSize.setFileSize(fileSizeFromEof, std::nullopt);
+ }
+ if (dp.cfg.indicCfg.eofRecvIndicRequired) {
+ dp.user.eofRecvIndication(getTransactionId());
+ }
+ if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
+ if (fsmRes.state == CfdpStates::BUSY_CLASS_1_NACKED) {
+ fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
+ } else if (fsmRes.state == CfdpStates::BUSY_CLASS_2_ACKED) {
+ fsmRes.step = TransactionStep::SENDING_ACK_PDU;
+ }
+ }
+ return returnvalue::OK;
+}
+
+ReturnValue_t cfdp::DestHandler::handleMetadataParseError(ReturnValue_t result,
+ const uint8_t* rawData, size_t maxSize) {
+ // TODO: try to extract destination ID for error
+ // TODO: Invalid metadata PDU.
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::warning << "Parsing Metadata PDU failed with code " << result << std::endl;
+#else
+#endif
+ PduHeaderReader headerReader(rawData, maxSize);
+ result = headerReader.parseData();
+ if (result != OK) {
+ // TODO: Now this really should not happen. Warning or error,
+ // yield or cache appropriate returnvalue
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::warning << "Parsing Header failed" << std::endl;
+#else
+#endif
+ // TODO: Trigger appropriate event
+ return result;
+ }
+ cfdp::EntityId destId;
+ headerReader.getDestId(destId);
+ RemoteEntityCfg* remoteCfg;
+ if (not dp.remoteCfgTable.getRemoteCfg(destId, &remoteCfg)) {
+// TODO: No remote config for dest ID. I consider this a configuration error, which is not
+// covered by the standard.
+// Warning or error, yield or cache appropriate returnvalue
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::warning << "No remote config exists for destination ID" << std::endl;
+#else
+#endif
+ // TODO: Trigger appropriate event
+ }
+ // TODO: Appropriate returnvalue?
+ return returnvalue::FAILED;
+}
+
+ReturnValue_t cfdp::DestHandler::startTransaction(MetadataPduReader& reader, MetadataInfo& info) {
+ if (fsmRes.state != CfdpStates::IDLE) {
+ // According to standard, discard metadata PDU if we are busy
+ return OK;
+ }
+ ReturnValue_t result = OK;
+ size_t sourceNameSize = 0;
+
+ const uint8_t* sourceNamePtr = info.getSourceFileName().getValue(&sourceNameSize);
+ if (sourceNameSize + 1 > tp.sourceName.size()) {
+ fileErrorHandler(events::FILENAME_TOO_LARGE_ERROR, 0, "source filename too large");
+ return FAILED;
+ }
+ std::memcpy(tp.sourceName.data(), sourceNamePtr, sourceNameSize);
+ tp.sourceName[sourceNameSize] = '\0';
+ size_t destNameSize = 0;
+ const uint8_t* destNamePtr = info.getDestFileName().getValue(&destNameSize);
+ if (destNameSize + 1 > tp.destName.size()) {
+ fileErrorHandler(events::FILENAME_TOO_LARGE_ERROR, 0, "dest filename too large");
+ return FAILED;
+ }
+ std::memcpy(tp.destName.data(), destNamePtr, destNameSize);
+ tp.destName[destNameSize] = '\0';
+
+ // If both dest name size and source name size are 0, we are dealing with a metadata only PDU,
+ // so there is no need to create a file or truncate an existing file
+ if (destNameSize > 0 and sourceNameSize > 0) {
+ FilesystemParams fparams(tp.destName.data());
+ // handling to allow only specifying target directory. Example:
+ // Source path /test/hello.txt, dest path /tmp -> dest path /tmp/hello.txt
+ if (dp.user.vfs.isDirectory(tp.destName.data())) {
+ result = tryBuildingAbsoluteDestName(destNameSize);
+ if (result != OK) {
+ return result;
+ }
+ }
+ if (dp.user.vfs.fileExists(fparams)) {
+ result = dp.user.vfs.truncateFile(fparams);
+ if (result != returnvalue::OK) {
+ fileErrorHandler(events::FILESTORE_ERROR, result, "file truncation error");
+ return FAILED;
+ // TODO: Relevant for filestore rejection error?
+ }
+ } else {
+ result = dp.user.vfs.createFile(fparams);
+ if (result != OK) {
+ fileErrorHandler(events::FILESTORE_ERROR, result, "file creation error");
+ return FAILED;
+ // TODO: Relevant for filestore rejection error?
+ }
+ }
+ }
+ EntityId sourceId;
+ reader.getSourceId(sourceId);
+ if (not dp.remoteCfgTable.getRemoteCfg(sourceId, &tp.remoteCfg)) {
+ // TODO: Warning, event etc.
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::warning << "cfdp::DestHandler" << __func__
+ << ": No remote configuration found for destination ID "
+ << tp.pduConf.sourceId.getValue() << std::endl;
+#endif
+ return FAILED;
+ }
+ fsmRes.step = TransactionStep::TRANSACTION_START;
+ if (reader.getTransmissionMode() == TransmissionMode::UNACKNOWLEDGED) {
+ fsmRes.state = CfdpStates::BUSY_CLASS_1_NACKED;
+ } else if (reader.getTransmissionMode() == TransmissionMode::ACKNOWLEDGED) {
+ fsmRes.state = CfdpStates::BUSY_CLASS_2_ACKED;
+ }
+ tp.checksumType = info.getChecksumType();
+ tp.closureRequested = info.isClosureRequested();
+ reader.fillConfig(tp.pduConf);
+ tp.pduConf.direction = Direction::TOWARDS_SENDER;
+ tp.transactionId.entityId = tp.pduConf.sourceId;
+ tp.transactionId.seqNum = tp.pduConf.seqNum;
+ fsmRes.step = TransactionStep::RECEIVING_FILE_DATA_PDUS;
+ MetadataRecvdParams params(tp.transactionId, tp.pduConf.sourceId);
+ params.fileSize = tp.fileSize.getSize();
+ params.destFileName = tp.destName.data();
+ params.sourceFileName = tp.sourceName.data();
+ params.msgsToUserArray = dynamic_cast(userTlvVec.data());
+ params.msgsToUserLen = info.getOptionsLen();
+ dp.user.metadataRecvdIndication(params);
+ return result;
+}
+
+cfdp::CfdpStates cfdp::DestHandler::getCfdpState() const { return fsmRes.state; }
+
+ReturnValue_t cfdp::DestHandler::handleTransferCompletion() {
+ ReturnValue_t result;
+ if (tp.checksumType != ChecksumType::NULL_CHECKSUM) {
+ result = checksumVerification();
+ if (result != OK) {
+ // TODO: Warning / error handling?
+ }
+ } else {
+ tp.conditionCode = ConditionCode::NO_ERROR;
+ }
+ result = noticeOfCompletion();
+ if (result != OK) {
+ }
+ if (fsmRes.state == CfdpStates::BUSY_CLASS_1_NACKED) {
+ if (tp.closureRequested) {
+ fsmRes.step = TransactionStep::SENDING_FINISHED_PDU;
+ } else {
+ finish();
+ }
+ } else if (fsmRes.state == CfdpStates::BUSY_CLASS_2_ACKED) {
+ fsmRes.step = TransactionStep::SENDING_FINISHED_PDU;
+ }
+ return OK;
+}
+
+ReturnValue_t cfdp::DestHandler::tryBuildingAbsoluteDestName(size_t destNameSize) {
+ char baseNameBuf[tp.destName.size()]{};
+ FilesystemParams fparamsSrc(tp.sourceName.data());
+ size_t baseNameLen = 0;
+ ReturnValue_t result =
+ dp.user.vfs.getBaseFilename(fparamsSrc, baseNameBuf, sizeof(baseNameBuf), baseNameLen);
+ if (result != returnvalue::OK or baseNameLen == 0) {
+ fileErrorHandler(events::FILENAME_TOO_LARGE_ERROR, 0, "error retrieving source base name");
+ return FAILED;
+ }
+ // Destination name + slash + base name + null termination
+ if (destNameSize + 1 + baseNameLen + 1 > tp.destName.size()) {
+ fileErrorHandler(events::FILENAME_TOO_LARGE_ERROR, 0,
+ "dest filename too large after adding source base name");
+ return FAILED;
+ }
+ tp.destName[destNameSize++] = '/';
+ std::memcpy(tp.destName.data() + destNameSize, baseNameBuf, baseNameLen);
+ destNameSize += baseNameLen;
+ tp.destName[destNameSize++] = '\0';
+ return OK;
+}
+
+void cfdp::DestHandler::fileErrorHandler(Event event, ReturnValue_t result, const char* info) {
+ fp.eventReporter->forwardEvent(events::FILENAME_TOO_LARGE_ERROR,
+ static_cast(fsmRes.step), result);
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::warning << "cfdp::DestHandler: " << info << std::endl;
+#endif
+}
+
+void cfdp::DestHandler::finish() {
+ tp.reset();
+ dp.packetListRef.clear();
+ fsmRes.state = CfdpStates::IDLE;
+ fsmRes.step = TransactionStep::IDLE;
+}
+
+ReturnValue_t cfdp::DestHandler::checksumVerification() {
+ std::array buf{};
+ // TODO: Checksum verification and notice of completion
+ etl::crc32 crcCalc;
+ uint64_t currentOffset = 0;
+ FileOpParams params(tp.destName.data(), tp.fileSize.value());
+ while (currentOffset < tp.fileSize.value()) {
+ uint64_t readLen;
+ if (currentOffset + buf.size() > tp.fileSize.value()) {
+ readLen = tp.fileSize.value() - currentOffset;
+ } else {
+ readLen = buf.size();
+ }
+ if (readLen > 0) {
+ params.offset = currentOffset;
+ params.size = readLen;
+ auto result = dp.user.vfs.readFromFile(params, buf.data(), buf.size());
+ if (result != OK) {
+ // TODO: I think this is a case for a filestore rejection, but it might sense to print
+ // a warning or trigger an event because this should generally not happen
+ return FAILED;
+ }
+ crcCalc.add(buf.begin(), buf.begin() + readLen);
+ }
+ currentOffset += readLen;
+ }
+
+ uint32_t value = crcCalc.value();
+ if (value == tp.crc) {
+ tp.conditionCode = ConditionCode::NO_ERROR;
+ tp.deliveryCode = FileDeliveryCode::DATA_COMPLETE;
+ } else {
+ // TODO: Proper error handling
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::warning << "CRC check for file " << tp.destName.data() << " failed" << std::endl;
+#endif
+ tp.conditionCode = ConditionCode::FILE_CHECKSUM_FAILURE;
+ }
+ return OK;
+}
+
+ReturnValue_t cfdp::DestHandler::noticeOfCompletion() {
+ if (dp.cfg.indicCfg.transactionFinishedIndicRequired) {
+ TransactionFinishedParams params(tp.transactionId, tp.conditionCode, tp.deliveryCode,
+ tp.deliveryStatus);
+ dp.user.transactionFinishedIndication(params);
+ }
+ return OK;
+}
+
+ReturnValue_t cfdp::DestHandler::sendFinishedPdu() {
+ FinishedInfo info(tp.conditionCode, tp.deliveryCode, tp.deliveryStatus);
+ FinishPduCreator finishedPdu(tp.pduConf, info);
+ store_address_t storeId;
+ uint8_t* dataPtr = nullptr;
+ ReturnValue_t result =
+ fp.tmStore->getFreeElement(&storeId, finishedPdu.getSerializedSize(), &dataPtr);
+ if (result != OK) {
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::warning << "cfdp::DestHandler:sendFinishedPdu: Getting store slot failed" << std::endl;
+#endif
+ fp.eventReporter->forwardEvent(events::STORE_ERROR, result, 0);
+ return result;
+ }
+ size_t serLen = 0;
+ result = finishedPdu.serialize(dataPtr, serLen, finishedPdu.getSerializedSize());
+ if (result != OK) {
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::warning << "cfdp::DestHandler::sendFinishedPdu: Serializing Finished PDU failed"
+ << std::endl;
+#endif
+ fp.eventReporter->forwardEvent(events::SERIALIZATION_ERROR, result, 0);
+ return result;
+ }
+ TmTcMessage msg(storeId);
+ result = fp.msgQueue->sendMessage(fp.packetDest.getReportReceptionQueue(), &msg);
+ if (result != OK) {
+#if FSFW_CPP_OSTREAM_ENABLED == 1
+ sif::warning << "cfdp::DestHandler::sendFinishedPdu: Sending PDU failed" << std::endl;
+#endif
+ fp.eventReporter->forwardEvent(events::MSG_QUEUE_ERROR, result, 0);
+ return result;
+ }
+ fsmRes.packetsSent++;
+ return OK;
+}
+
+cfdp::DestHandler::TransactionStep cfdp::DestHandler::getTransactionStep() const {
+ return fsmRes.step;
+}
+
+const cfdp::DestHandler::FsmResult& cfdp::DestHandler::updateFsmRes(uint8_t errors) {
+ fsmRes.errors = errors;
+ fsmRes.result = OK;
+ if (fsmRes.errors > 0) {
+ fsmRes.result = FAILED;
+ }
+ return fsmRes;
+}
+
+const cfdp::TransactionId& cfdp::DestHandler::getTransactionId() const { return tp.transactionId; }
+
+void cfdp::DestHandler::checkAndHandleError(ReturnValue_t result, uint8_t& errorIdx) {
+ if (result != OK and errorIdx < 3) {
+ fsmRes.errorCodes[errorIdx] = result;
+ errorIdx++;
+ }
+}
+
+void cfdp::DestHandler::setMsgQueue(MessageQueueIF& queue) { fp.msgQueue = &queue; }
+
+void cfdp::DestHandler::setEventReporter(EventReportingProxyIF& reporter) {
+ fp.eventReporter = &reporter;
+}
+
+const cfdp::DestHandlerParams& cfdp::DestHandler::getDestHandlerParams() const { return dp; }
+
+StorageManagerIF* cfdp::DestHandler::getTmStore() const { return fp.tmStore; }
+StorageManagerIF* cfdp::DestHandler::getTcStore() const { return fp.tcStore; }
diff --git a/src/fsfw/cfdp/handler/DestHandler.h b/src/fsfw/cfdp/handler/DestHandler.h
new file mode 100644
index 000000000..9057b3f56
--- /dev/null
+++ b/src/fsfw/cfdp/handler/DestHandler.h
@@ -0,0 +1,206 @@
+#ifndef FSFW_CFDP_CFDPDESTHANDLER_H
+#define FSFW_CFDP_CFDPDESTHANDLER_H
+
+#include
+#include
+
+#include
+#include
+
+#include "RemoteConfigTableIF.h"
+#include "UserBase.h"
+#include "defs.h"
+#include "fsfw/cfdp/handler/mib.h"
+#include "fsfw/cfdp/pdu/MetadataPduReader.h"
+#include "fsfw/cfdp/pdu/PduConfig.h"
+#include "fsfw/container/DynamicFIFO.h"
+#include "fsfw/storagemanager/StorageManagerIF.h"
+#include "fsfw/storagemanager/storeAddress.h"
+#include "fsfw/tmtcservices/AcceptsTelemetryIF.h"
+
+namespace cfdp {
+
+struct PacketInfo {
+ PacketInfo(PduType type, store_address_t storeId,
+ std::optional directive = std::nullopt)
+ : pduType(type), directiveType(directive), storeId(storeId) {}
+
+ PduType pduType = PduType::FILE_DATA;
+ std::optional directiveType = FileDirective::INVALID_DIRECTIVE;
+ store_address_t storeId = store_address_t::invalid();
+ PacketInfo() = default;
+};
+
+template
+using LostSegmentsList = etl::set, SIZE>;
+template
+using PacketInfoList = etl::list;
+using LostSegmentsListBase = etl::iset>;
+using PacketInfoListBase = etl::ilist;
+
+struct DestHandlerParams {
+ DestHandlerParams(LocalEntityCfg cfg, UserBase& user, RemoteConfigTableIF& remoteCfgTable,
+ PacketInfoListBase& packetList,
+ // TODO: This container can potentially take tons of space. For a better
+ // memory efficient implementation, an additional abstraction could be
+ // be used so users can use uint32_t as the pair type
+ LostSegmentsListBase& lostSegmentsContainer)
+ : cfg(std::move(cfg)),
+ user(user),
+ remoteCfgTable(remoteCfgTable),
+ packetListRef(packetList),
+ lostSegmentsContainer(lostSegmentsContainer) {}
+
+ LocalEntityCfg cfg;
+ UserBase& user;
+ RemoteConfigTableIF& remoteCfgTable;
+
+ PacketInfoListBase& packetListRef;
+ LostSegmentsListBase& lostSegmentsContainer;
+ uint8_t maxTlvsInOnePdu = 10;
+ size_t maxFilenameLen = 255;
+};
+
+struct FsfwParams {
+ FsfwParams(AcceptsTelemetryIF& packetDest, MessageQueueIF* msgQueue,
+ EventReportingProxyIF* eventReporter, StorageManagerIF& tcStore,
+ StorageManagerIF& tmStore)
+ : FsfwParams(packetDest, msgQueue, eventReporter) {
+ this->tcStore = &tcStore;
+ this->tmStore = &tmStore;
+ }
+
+ FsfwParams(AcceptsTelemetryIF& packetDest, MessageQueueIF* msgQueue,
+ EventReportingProxyIF* eventReporter)
+ : packetDest(packetDest), msgQueue(msgQueue), eventReporter(eventReporter) {}
+ AcceptsTelemetryIF& packetDest;
+ MessageQueueIF* msgQueue;
+ EventReportingProxyIF* eventReporter = nullptr;
+ StorageManagerIF* tcStore = nullptr;
+ StorageManagerIF* tmStore = nullptr;
+};
+
+enum class CallStatus { DONE, CALL_AFTER_DELAY, CALL_AGAIN };
+
+class DestHandler {
+ public:
+ enum class TransactionStep : uint8_t {
+ IDLE = 0,
+ TRANSACTION_START = 1,
+ RECEIVING_FILE_DATA_PDUS = 2,
+ SENDING_ACK_PDU = 3,
+ TRANSFER_COMPLETION = 4,
+ SENDING_FINISHED_PDU = 5
+ };
+
+ struct FsmResult {
+ public:
+ ReturnValue_t result = returnvalue::OK;
+ CallStatus callStatus = CallStatus::CALL_AFTER_DELAY;
+ TransactionStep step = TransactionStep::IDLE;
+ CfdpStates state = CfdpStates::IDLE;
+ uint32_t packetsSent = 0;
+ uint8_t errors = 0;
+ std::array errorCodes = {};
+ void resetOfIteration() {
+ result = returnvalue::OK;
+ callStatus = CallStatus::CALL_AFTER_DELAY;
+ packetsSent = 0;
+ errors = 0;
+ errorCodes.fill(returnvalue::OK);
+ }
+ };
+ /**
+ * Will be returned if it is advisable to call the state machine operation call again
+ */
+ ReturnValue_t PARTIAL_SUCCESS = returnvalue::makeCode(0, 2);
+ ReturnValue_t FAILURE = returnvalue::makeCode(0, 3);
+ explicit DestHandler(DestHandlerParams handlerParams, FsfwParams fsfwParams);
+
+ /**
+ *
+ * @return
+ * - @c returnvalue::OK State machine OK for this execution cycle
+ * - @c CALL_FSM_AGAIN State machine should be called again.
+ */
+ const FsmResult& performStateMachine();
+ void setMsgQueue(MessageQueueIF& queue);
+ void setEventReporter(EventReportingProxyIF& reporter);
+
+ ReturnValue_t passPacket(PacketInfo packet);
+
+ ReturnValue_t initialize();
+
+ [[nodiscard]] CfdpStates getCfdpState() const;
+ [[nodiscard]] TransactionStep getTransactionStep() const;
+ [[nodiscard]] const TransactionId& getTransactionId() const;
+ [[nodiscard]] const DestHandlerParams& getDestHandlerParams() const;
+ [[nodiscard]] StorageManagerIF* getTcStore() const;
+ [[nodiscard]] StorageManagerIF* getTmStore() const;
+
+ private:
+ struct TransactionParams {
+ // Initialize char vectors with length + 1 for 0 termination
+ explicit TransactionParams(size_t maxFileNameLen)
+ : sourceName(maxFileNameLen + 1), destName(maxFileNameLen + 1) {}
+
+ void reset() {
+ pduConf = PduConfig();
+ transactionId = TransactionId();
+ std::fill(sourceName.begin(), sourceName.end(), '\0');
+ std::fill(destName.begin(), destName.end(), '\0');
+ fileSize.setFileSize(0, false);
+ conditionCode = ConditionCode::NO_ERROR;
+ deliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
+ deliveryStatus = FileDeliveryStatus::DISCARDED_DELIBERATELY;
+ crc = 0;
+ progress = 0;
+ remoteCfg = nullptr;
+ closureRequested = false;
+ vfsErrorCount = 0;
+ checksumType = ChecksumType::NULL_CHECKSUM;
+ }
+
+ ChecksumType checksumType = ChecksumType::NULL_CHECKSUM;
+ bool closureRequested = false;
+ uint16_t vfsErrorCount = 0;
+ std::vector sourceName;
+ std::vector destName;
+ cfdp::FileSize fileSize;
+ TransactionId transactionId;
+ PduConfig pduConf;
+ ConditionCode conditionCode = ConditionCode::NO_ERROR;
+ FileDeliveryCode deliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
+ FileDeliveryStatus deliveryStatus = FileDeliveryStatus::DISCARDED_DELIBERATELY;
+ uint32_t crc = 0;
+ uint64_t progress = 0;
+ RemoteEntityCfg* remoteCfg = nullptr;
+ };
+
+ std::vector tlvVec;
+ std::vector userTlvVec;
+ DestHandlerParams dp;
+ FsfwParams fp;
+ TransactionParams tp;
+ FsmResult fsmRes;
+
+ ReturnValue_t startTransaction(MetadataPduReader& reader, MetadataInfo& info);
+ ReturnValue_t handleMetadataPdu(const PacketInfo& info);
+ ReturnValue_t handleFileDataPdu(const PacketInfo& info);
+ ReturnValue_t handleEofPdu(const PacketInfo& info);
+ ReturnValue_t handleMetadataParseError(ReturnValue_t result, const uint8_t* rawData,
+ size_t maxSize);
+ ReturnValue_t handleTransferCompletion();
+ ReturnValue_t tryBuildingAbsoluteDestName(size_t destNameSize);
+ ReturnValue_t sendFinishedPdu();
+ ReturnValue_t noticeOfCompletion();
+ ReturnValue_t checksumVerification();
+ void fileErrorHandler(Event event, ReturnValue_t result, const char* info);
+ const FsmResult& updateFsmRes(uint8_t errors);
+ void checkAndHandleError(ReturnValue_t result, uint8_t& errorIdx);
+ void finish();
+};
+
+} // namespace cfdp
+
+#endif // FSFW_CFDP_CFDPDESTHANDLER_H
diff --git a/src/fsfw/cfdp/handler/SourceHandler.cpp b/src/fsfw/cfdp/handler/SourceHandler.cpp
new file mode 100644
index 000000000..513b25f37
--- /dev/null
+++ b/src/fsfw/cfdp/handler/SourceHandler.cpp
@@ -0,0 +1 @@
+#include "SourceHandler.h"
diff --git a/src/fsfw/cfdp/handler/SourceHandler.h b/src/fsfw/cfdp/handler/SourceHandler.h
new file mode 100644
index 000000000..319cf2583
--- /dev/null
+++ b/src/fsfw/cfdp/handler/SourceHandler.h
@@ -0,0 +1,6 @@
+#ifndef FSFW_CFDP_CFDPSOURCEHANDLER_H
+#define FSFW_CFDP_CFDPSOURCEHANDLER_H
+
+class SourceHandler {};
+
+#endif // FSFW_CFDP_CFDPSOURCEHANDLER_H
diff --git a/src/fsfw/cfdp/handler/defs.h b/src/fsfw/cfdp/handler/defs.h
index 9e837a96b..5f17ca2dd 100644
--- a/src/fsfw/cfdp/handler/defs.h
+++ b/src/fsfw/cfdp/handler/defs.h
@@ -5,5 +5,18 @@ namespace cfdp {
enum class CfdpStates { IDLE, BUSY_CLASS_1_NACKED, BUSY_CLASS_2_ACKED, SUSPENDED };
-}
+static constexpr uint8_t SSID = SUBSYSTEM_ID::CFDP;
+
+namespace events {
+
+static constexpr Event STORE_ERROR = event::makeEvent(SSID, 0, severity::LOW);
+static constexpr Event MSG_QUEUE_ERROR = event::makeEvent(SSID, 1, severity::LOW);
+static constexpr Event SERIALIZATION_ERROR = event::makeEvent(SSID, 2, severity::LOW);
+static constexpr Event FILESTORE_ERROR = event::makeEvent(SSID, 3, severity::LOW);
+//! [EXPORT] : [COMMENT] P1: Transaction step ID, P2: 0 for source file name, 1 for dest file name
+static constexpr Event FILENAME_TOO_LARGE_ERROR = event::makeEvent(SSID, 4, severity::LOW);
+
+} // namespace events
+
+} // namespace cfdp
#endif // FSFW_CFDP_HANDLER_DEFS_H
diff --git a/src/fsfw/cfdp/pdu/HeaderCreator.cpp b/src/fsfw/cfdp/pdu/HeaderCreator.cpp
index 296885756..2db3953c9 100644
--- a/src/fsfw/cfdp/pdu/HeaderCreator.cpp
+++ b/src/fsfw/cfdp/pdu/HeaderCreator.cpp
@@ -24,8 +24,8 @@ ReturnValue_t HeaderCreator::serialize(uint8_t **buffer, size_t *size, size_t ma
*buffer += 1;
**buffer = pduDataFieldLen & 0x00ff;
*buffer += 1;
- **buffer = segmentationCtrl << 7 | pduConf.sourceId.getWidth() << 4 | segmentMetadataFlag << 3 |
- pduConf.seqNum.getWidth();
+ **buffer = segmentationCtrl << 7 | ((pduConf.sourceId.getWidth() - 1) << 4) |
+ segmentMetadataFlag << 3 | (pduConf.seqNum.getWidth() - 1);
*buffer += 1;
*size += 4;
ReturnValue_t result = pduConf.sourceId.serialize(buffer, size, maxSize, streamEndianness);
diff --git a/src/fsfw/cfdp/pdu/HeaderReader.cpp b/src/fsfw/cfdp/pdu/HeaderReader.cpp
index 9edf2394d..de3d29066 100644
--- a/src/fsfw/cfdp/pdu/HeaderReader.cpp
+++ b/src/fsfw/cfdp/pdu/HeaderReader.cpp
@@ -78,11 +78,11 @@ cfdp::SegmentationControl PduHeaderReader::getSegmentationControl() const {
}
cfdp::WidthInBytes PduHeaderReader::getLenEntityIds() const {
- return static_cast((pointers.fixedHeader->fourthByte >> 4) & 0x07);
+ return static_cast(((pointers.fixedHeader->fourthByte >> 4) & 0b111) + 1);
}
cfdp::WidthInBytes PduHeaderReader::getLenSeqNum() const {
- return static_cast(pointers.fixedHeader->fourthByte & 0x07);
+ return static_cast((pointers.fixedHeader->fourthByte & 0b111) + 1);
}
cfdp::SegmentMetadataFlag PduHeaderReader::getSegmentMetadataFlag() const {
diff --git a/src/fsfw/controller/ControllerBase.cpp b/src/fsfw/controller/ControllerBase.cpp
index a9d19ef28..2c151f5a9 100644
--- a/src/fsfw/controller/ControllerBase.cpp
+++ b/src/fsfw/controller/ControllerBase.cpp
@@ -4,48 +4,31 @@
#include "fsfw/ipc/QueueFactory.h"
#include "fsfw/objectmanager/ObjectManager.h"
#include "fsfw/subsystem/SubsystemBase.h"
+#include "fsfw/subsystem/helper.h"
-ControllerBase::ControllerBase(object_id_t setObjectId, object_id_t parentId,
- size_t commandQueueDepth)
+ControllerBase::ControllerBase(object_id_t setObjectId, size_t commandQueueDepth)
: SystemObject(setObjectId),
- parentId(parentId),
mode(MODE_OFF),
submode(SUBMODE_NONE),
modeHelper(this),
healthHelper(this, setObjectId) {
- commandQueue = QueueFactory::instance()->createMessageQueue(commandQueueDepth);
+ auto mqArgs = MqArgs(setObjectId, static_cast(this));
+ commandQueue = QueueFactory::instance()->createMessageQueue(
+ commandQueueDepth, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs);
}
ControllerBase::~ControllerBase() { QueueFactory::instance()->deleteMessageQueue(commandQueue); }
ReturnValue_t ControllerBase::initialize() {
- ReturnValue_t result = SystemObject::initialize();
+ ReturnValue_t result = modeHelper.initialize();
if (result != returnvalue::OK) {
return result;
}
-
- MessageQueueId_t parentQueue = 0;
- if (parentId != objects::NO_OBJECT) {
- auto* parent = ObjectManager::instance()->get(parentId);
- if (parent == nullptr) {
- return returnvalue::FAILED;
- }
- parentQueue = parent->getCommandQueue();
-
- parent->registerChild(getObjectId());
- }
-
- result = healthHelper.initialize(parentQueue);
+ result = healthHelper.initialize();
if (result != returnvalue::OK) {
return result;
}
-
- result = modeHelper.initialize(parentQueue);
- if (result != returnvalue::OK) {
- return result;
- }
-
- return returnvalue::OK;
+ return SystemObject::initialize();
}
MessageQueueId_t ControllerBase::getCommandQueue() const { return commandQueue->getId(); }
@@ -75,7 +58,7 @@ void ControllerBase::handleQueue() {
void ControllerBase::startTransition(Mode_t mode_, Submode_t submode_) {
changeHK(this->mode, this->submode, false);
- triggerEvent(CHANGING_MODE, mode, submode);
+ triggerEvent(CHANGING_MODE, mode_, submode_);
mode = mode_;
submode = submode_;
modeHelper.modeChanged(mode, submode);
@@ -118,3 +101,13 @@ void ControllerBase::setTaskIF(PeriodicTaskIF* task_) { executingTask = task_; }
void ControllerBase::changeHK(Mode_t mode_, Submode_t submode_, bool enable) {}
ReturnValue_t ControllerBase::initializeAfterTaskCreation() { return returnvalue::OK; }
+
+const HasHealthIF* ControllerBase::getOptHealthIF() const { return this; }
+
+const HasModesIF& ControllerBase::getModeIF() const { return *this; }
+
+ModeTreeChildIF& ControllerBase::getModeTreeChildIF() { return *this; }
+
+ReturnValue_t ControllerBase::connectModeTreeParent(HasModeTreeChildrenIF& parent) {
+ return modetree::connectModeTreeParent(parent, *this, &healthHelper, modeHelper);
+}
diff --git a/src/fsfw/controller/ControllerBase.h b/src/fsfw/controller/ControllerBase.h
index 56c28585b..da140e494 100644
--- a/src/fsfw/controller/ControllerBase.h
+++ b/src/fsfw/controller/ControllerBase.h
@@ -6,6 +6,9 @@
#include "fsfw/modes/HasModesIF.h"
#include "fsfw/modes/ModeHelper.h"
#include "fsfw/objectmanager/SystemObject.h"
+#include "fsfw/subsystem/HasModeTreeChildrenIF.h"
+#include "fsfw/subsystem/ModeTreeChildIF.h"
+#include "fsfw/subsystem/ModeTreeConnectionIF.h"
#include "fsfw/tasks/ExecutableObjectIF.h"
#include "fsfw/tasks/PeriodicTaskIF.h"
@@ -18,13 +21,18 @@
class ControllerBase : public HasModesIF,
public HasHealthIF,
public ExecutableObjectIF,
+ public ModeTreeChildIF,
+ public ModeTreeConnectionIF,
public SystemObject {
public:
static const Mode_t MODE_NORMAL = 2;
- ControllerBase(object_id_t setObjectId, object_id_t parentId, size_t commandQueueDepth = 3);
+ ControllerBase(object_id_t setObjectId, size_t commandQueueDepth = 3);
~ControllerBase() override;
+ ReturnValue_t connectModeTreeParent(HasModeTreeChildrenIF &parent) override;
+ ModeTreeChildIF &getModeTreeChildIF() override;
+
/** SystemObject override */
ReturnValue_t initialize() override;
@@ -38,6 +46,8 @@ class ControllerBase : public HasModesIF,
ReturnValue_t performOperation(uint8_t opCode) override;
void setTaskIF(PeriodicTaskIF *task) override;
ReturnValue_t initializeAfterTaskCreation() override;
+ const HasHealthIF *getOptHealthIF() const override;
+ const HasModesIF &getModeIF() const override;
protected:
/**
@@ -56,8 +66,6 @@ class ControllerBase : public HasModesIF,
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t *msToReachTheMode) override = 0;
- const object_id_t parentId;
-
Mode_t mode;
Submode_t submode;
diff --git a/src/fsfw/controller/ExtendedControllerBase.cpp b/src/fsfw/controller/ExtendedControllerBase.cpp
index 4d5c90c84..58db35636 100644
--- a/src/fsfw/controller/ExtendedControllerBase.cpp
+++ b/src/fsfw/controller/ExtendedControllerBase.cpp
@@ -1,8 +1,7 @@
#include "fsfw/controller/ExtendedControllerBase.h"
-ExtendedControllerBase::ExtendedControllerBase(object_id_t objectId, object_id_t parentId,
- size_t commandQueueDepth)
- : ControllerBase(objectId, parentId, commandQueueDepth),
+ExtendedControllerBase::ExtendedControllerBase(object_id_t objectId, size_t commandQueueDepth)
+ : ControllerBase(objectId, commandQueueDepth),
poolManager(this, commandQueue),
actionHelper(this, commandQueue) {}
diff --git a/src/fsfw/controller/ExtendedControllerBase.h b/src/fsfw/controller/ExtendedControllerBase.h
index b5583a880..04a795281 100644
--- a/src/fsfw/controller/ExtendedControllerBase.h
+++ b/src/fsfw/controller/ExtendedControllerBase.h
@@ -17,7 +17,7 @@ class ExtendedControllerBase : public ControllerBase,
public HasActionsIF,
public HasLocalDataPoolIF {
public:
- ExtendedControllerBase(object_id_t objectId, object_id_t parentId, size_t commandQueueDepth = 3);
+ ExtendedControllerBase(object_id_t objectId, size_t commandQueueDepth = 3);
~ExtendedControllerBase() override;
/* SystemObjectIF overrides */
diff --git a/src/fsfw/coordinates/Sgp4Propagator.cpp b/src/fsfw/coordinates/Sgp4Propagator.cpp
index e79ffef5e..62c2670e8 100644
--- a/src/fsfw/coordinates/Sgp4Propagator.cpp
+++ b/src/fsfw/coordinates/Sgp4Propagator.cpp
@@ -166,9 +166,9 @@ ReturnValue_t Sgp4Propagator::propagate(double* position, double* velocity, time
timeval timeSinceEpoch = time - epoch;
double minutesSinceEpoch = timeSinceEpoch.tv_sec / 60. + timeSinceEpoch.tv_usec / 60000000.;
- double yearsSinceEpoch = minutesSinceEpoch / 60 / 24 / 365;
+ double monthsSinceEpoch = minutesSinceEpoch / 60 / 24 / 30;
- if ((yearsSinceEpoch > 1) || (yearsSinceEpoch < -1)) {
+ if ((monthsSinceEpoch > 1) || (monthsSinceEpoch < -1)) {
return TLE_TOO_OLD;
}
diff --git a/src/fsfw/datapoollocal/LocalDataPoolManager.cpp b/src/fsfw/datapoollocal/LocalDataPoolManager.cpp
index fa76d2a7c..32de8e6bc 100644
--- a/src/fsfw/datapoollocal/LocalDataPoolManager.cpp
+++ b/src/fsfw/datapoollocal/LocalDataPoolManager.cpp
@@ -70,8 +70,7 @@ ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) {
return returnvalue::OK;
}
-ReturnValue_t LocalDataPoolManager::initializeAfterTaskCreation(uint8_t nonDiagInvlFactor) {
- setNonDiagnosticIntervalFactor(nonDiagInvlFactor);
+ReturnValue_t LocalDataPoolManager::initializeAfterTaskCreation() {
return initializeHousekeepingPoolEntriesOnce();
}
@@ -506,9 +505,9 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(CommandMessage* me
float newCollIntvl = 0;
HousekeepingMessage::getCollectionIntervalModificationCommand(message, &newCollIntvl);
if (command == HousekeepingMessage::MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL) {
- result = changeCollectionInterval(sid, newCollIntvl, true);
+ result = changeCollectionInterval(sid, newCollIntvl);
} else {
- result = changeCollectionInterval(sid, newCollIntvl, false);
+ result = changeCollectionInterval(sid, newCollIntvl);
}
break;
}
@@ -570,6 +569,10 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(CommandMessage* me
CommandMessage reply;
if (result != returnvalue::OK) {
+ if (result == WRONG_HK_PACKET_TYPE) {
+ printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleHousekeepingMessage",
+ WRONG_HK_PACKET_TYPE);
+ }
HousekeepingMessage::setHkRequestFailureReply(&reply, sid, result);
} else {
HousekeepingMessage::setHkRequestSuccessReply(&reply, sid);
@@ -657,10 +660,6 @@ ReturnValue_t LocalDataPoolManager::serializeHkPacketIntoStore(HousekeepingPacke
return hkPacket.serialize(&dataPtr, serializedSize, maxSize, SerializeIF::Endianness::MACHINE);
}
-void LocalDataPoolManager::setNonDiagnosticIntervalFactor(uint8_t nonDiagInvlFactor) {
- this->nonDiagnosticIntervalFactor = nonDiagInvlFactor;
-}
-
void LocalDataPoolManager::performPeriodicHkGeneration(HkReceiver& receiver) {
sid_t sid = receiver.dataId.sid;
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
@@ -714,15 +713,15 @@ ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid, bool ena
if ((LocalPoolDataSetAttorney::getReportingEnabled(*dataSet) and enable) or
(not LocalPoolDataSetAttorney::getReportingEnabled(*dataSet) and not enable)) {
- return REPORTING_STATUS_UNCHANGED;
+ return returnvalue::OK;
}
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, enable);
return returnvalue::OK;
}
-ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid, float newCollectionInterval,
- bool isDiagnostics) {
+ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
+ float newCollectionInterval) {
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
if (dataSet == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "changeCollectionInterval",
@@ -730,11 +729,6 @@ ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid, float ne
return DATASET_NOT_FOUND;
}
- bool targetIsDiagnostics = LocalPoolDataSetAttorney::isDiagnostics(*dataSet);
- if ((targetIsDiagnostics and not isDiagnostics) or (not targetIsDiagnostics and isDiagnostics)) {
- return WRONG_HK_PACKET_TYPE;
- }
-
PeriodicHousekeepingHelper* periodicHelper =
LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet);
@@ -825,6 +819,8 @@ void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
errorPrint = "Dataset not found";
} else if (error == POOLOBJECT_NOT_FOUND) {
errorPrint = "Pool Object not found";
+ } else if (error == WRONG_HK_PACKET_TYPE) {
+ errorPrint = "Wrong Packet Type";
} else if (error == returnvalue::FAILED) {
if (outputType == sif::OutputTypes::OUT_WARNING) {
errorPrint = "Generic Warning";
diff --git a/src/fsfw/datapoollocal/LocalDataPoolManager.h b/src/fsfw/datapoollocal/LocalDataPoolManager.h
index 8f369ea06..cd0d4f623 100644
--- a/src/fsfw/datapoollocal/LocalDataPoolManager.h
+++ b/src/fsfw/datapoollocal/LocalDataPoolManager.h
@@ -102,7 +102,7 @@ class LocalDataPoolManager : public ProvidesDataPoolSubscriptionIF, public Acces
* @param nonDiagInvlFactor
* @return
*/
- ReturnValue_t initializeAfterTaskCreation(uint8_t nonDiagInvlFactor = 5);
+ ReturnValue_t initializeAfterTaskCreation();
/**
* @brief This should be called in the periodic handler of the owner.
@@ -152,17 +152,6 @@ class LocalDataPoolManager : public ProvidesDataPoolSubscriptionIF, public Acces
MessageQueueId_t targetQueueId,
bool generateSnapshot) override;
- /**
- * Non-Diagnostics packets usually have a lower minimum sampling frequency
- * than diagnostic packets.
- * A factor can be specified to determine the minimum sampling frequency
- * for non-diagnostic packets. The minimum sampling frequency of the
- * diagnostics packets,which is usually jusst the period of the
- * performOperation calls, is multiplied with that factor.
- * @param factor
- */
- void setNonDiagnosticIntervalFactor(uint8_t nonDiagInvlFactor);
-
/**
* @brief The manager is also able to handle housekeeping messages.
* @details
@@ -185,6 +174,7 @@ class LocalDataPoolManager : public ProvidesDataPoolSubscriptionIF, public Acces
ReturnValue_t generateHousekeepingPacket(sid_t sid, LocalPoolDataSetBase* dataSet,
bool forDownlink,
MessageQueueId_t destination = MessageQueueIF::NO_QUEUE);
+ ReturnValue_t changeCollectionInterval(sid_t sid, float newCollectionInterval);
HasLocalDataPoolIF* getOwner();
@@ -348,8 +338,6 @@ class LocalDataPoolManager : public ProvidesDataPoolSubscriptionIF, public Acces
void performPeriodicHkGeneration(HkReceiver& hkReceiver);
ReturnValue_t togglePeriodicGeneration(sid_t sid, bool enable, bool isDiagnostics);
- ReturnValue_t changeCollectionInterval(sid_t sid, float newCollectionInterval,
- bool isDiagnostics);
ReturnValue_t generateSetStructurePacket(sid_t sid, bool isDiagnostics);
void handleHkUpdateResetListInsertion(DataType dataType, DataId dataId);
diff --git a/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp b/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp
index 38aad828b..289954462 100644
--- a/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp
+++ b/src/fsfw/datapoollocal/LocalPoolDataSetBase.cpp
@@ -250,9 +250,8 @@ void LocalPoolDataSetBase::setReportingEnabled(bool reportingEnabled) {
bool LocalPoolDataSetBase::getReportingEnabled() const { return reportingEnabled; }
void LocalPoolDataSetBase::initializePeriodicHelper(float collectionInterval,
- dur_millis_t minimumPeriodicInterval,
- uint8_t nonDiagIntervalFactor) {
- periodicHelper->initialize(collectionInterval, minimumPeriodicInterval, nonDiagIntervalFactor);
+ dur_millis_t minimumPeriodicInterval) {
+ periodicHelper->initialize(collectionInterval, minimumPeriodicInterval);
}
void LocalPoolDataSetBase::setChanged(bool changed) { this->changed = changed; }
diff --git a/src/fsfw/datapoollocal/LocalPoolDataSetBase.h b/src/fsfw/datapoollocal/LocalPoolDataSetBase.h
index 17cf8be2e..7aec77eaa 100644
--- a/src/fsfw/datapoollocal/LocalPoolDataSetBase.h
+++ b/src/fsfw/datapoollocal/LocalPoolDataSetBase.h
@@ -162,6 +162,7 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
object_id_t getCreatorObjectId();
bool getReportingEnabled() const;
+ void setReportingEnabled(bool enabled);
/**
* Returns the current periodic HK generation interval this set
@@ -189,10 +190,8 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
* Used for periodic generation.
*/
bool reportingEnabled = false;
- void setReportingEnabled(bool enabled);
- void initializePeriodicHelper(float collectionInterval, dur_millis_t minimumPeriodicInterval,
- uint8_t nonDiagIntervalFactor = 5);
+ void initializePeriodicHelper(float collectionInterval, dur_millis_t minimumPeriodicInterval);
/**
* If the valid state of a dataset is always relevant to the whole
diff --git a/src/fsfw/datapoollocal/internal/LocalPoolDataSetAttorney.h b/src/fsfw/datapoollocal/internal/LocalPoolDataSetAttorney.h
index e7dbd0c70..6f9a3b27b 100644
--- a/src/fsfw/datapoollocal/internal/LocalPoolDataSetAttorney.h
+++ b/src/fsfw/datapoollocal/internal/LocalPoolDataSetAttorney.h
@@ -12,10 +12,8 @@ class LocalPoolDataSetAttorney {
static bool isDiagnostics(LocalPoolDataSetBase& set) { return set.isDiagnostics(); }
static void initializePeriodicHelper(LocalPoolDataSetBase& set, float collectionInterval,
- uint32_t minimumPeriodicIntervalMs,
- uint8_t nonDiagIntervalFactor = 5) {
- set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs,
- nonDiagIntervalFactor);
+ uint32_t minimumPeriodicIntervalMs) {
+ set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs);
}
static void setReportingEnabled(LocalPoolDataSetBase& set, bool enabled) {
diff --git a/src/fsfw/devicehandlers/AssemblyBase.cpp b/src/fsfw/devicehandlers/AssemblyBase.cpp
index 63178ab95..14575e69f 100644
--- a/src/fsfw/devicehandlers/AssemblyBase.cpp
+++ b/src/fsfw/devicehandlers/AssemblyBase.cpp
@@ -1,7 +1,7 @@
#include "fsfw/devicehandlers/AssemblyBase.h"
-AssemblyBase::AssemblyBase(object_id_t objectId, object_id_t parentId, uint16_t commandQueueDepth)
- : SubsystemBase(objectId, parentId, MODE_OFF, commandQueueDepth),
+AssemblyBase::AssemblyBase(object_id_t objectId, uint16_t commandQueueDepth)
+ : SubsystemBase(objectId, MODE_OFF, commandQueueDepth),
internalState(STATE_NONE),
recoveryState(RECOVERY_IDLE),
recoveringDevice(childrenMap.end()),
@@ -26,11 +26,7 @@ void AssemblyBase::performChildOperation() {
void AssemblyBase::startTransition(Mode_t mode, Submode_t submode) {
doStartTransition(mode, submode);
- if (modeHelper.isForced()) {
- triggerEvent(FORCING_MODE, mode, submode);
- } else {
- triggerEvent(CHANGING_MODE, mode, submode);
- }
+ triggerModeHelperEvents(mode, submode);
}
void AssemblyBase::doStartTransition(Mode_t mode, Submode_t submode) {
@@ -77,9 +73,10 @@ bool AssemblyBase::handleChildrenChangedHealth() {
}
HealthState healthState = healthHelper.healthTable->getHealth(iter->first);
if (healthState == HasHealthIF::NEEDS_RECOVERY) {
- triggerEvent(TRYING_RECOVERY);
+ triggerEvent(TRYING_RECOVERY, iter->first, 0);
recoveryState = RECOVERY_STARTED;
recoveringDevice = iter;
+ // The user needs to take care of commanding the children off in commandChildren
doStartTransition(targetMode, targetSubmode);
} else {
triggerEvent(CHILD_CHANGED_HEALTH);
@@ -228,6 +225,9 @@ ReturnValue_t AssemblyBase::handleHealthReply(CommandMessage* message) {
bool AssemblyBase::checkAndHandleRecovery() {
switch (recoveryState) {
case RECOVERY_STARTED:
+ // The recovery was already start in #handleChildrenChangedHealth and we just need
+ // to wait for an off time period.
+ // TODO: make time period configurable
recoveryState = RECOVERY_WAIT;
recoveryOffTimer.resetTimer();
return true;
@@ -266,3 +266,11 @@ void AssemblyBase::overwriteDeviceHealth(object_id_t objectId, HasHealthIF::Heal
modeHelper.setForced(true);
sendHealthCommand(childrenMap[objectId].commandQueue, EXTERNAL_CONTROL);
}
+
+void AssemblyBase::triggerModeHelperEvents(Mode_t mode, Submode_t submode) {
+ if (modeHelper.isForced()) {
+ triggerEvent(FORCING_MODE, mode, submode);
+ } else {
+ triggerEvent(CHANGING_MODE, mode, submode);
+ }
+}
diff --git a/src/fsfw/devicehandlers/AssemblyBase.h b/src/fsfw/devicehandlers/AssemblyBase.h
index ec0847f16..5e0d826f1 100644
--- a/src/fsfw/devicehandlers/AssemblyBase.h
+++ b/src/fsfw/devicehandlers/AssemblyBase.h
@@ -12,7 +12,8 @@
* Documentation: Dissertation Baetz p.156, 157.
*
* This class reduces the complexity of controller components which would
- * otherwise be needed for the handling of redundant devices.
+ * otherwise be needed for the handling of redundant devices. However, it can also be used to
+ * manage the mode keeping and recovery of non-redundant devices
*
* The template class monitors mode and health state of its children
* and checks availability of devices on every detected change.
@@ -26,11 +27,9 @@
*
* Important:
*
- * The implementation must call registerChild(object_id_t child)
- * for all commanded children during initialization.
+ * The implementation must call #registerChild for all commanded children during initialization.
* The implementation must call the initialization function of the base class.
* (This will call the function in SubsystemBase)
- *
*/
class AssemblyBase : public SubsystemBase {
public:
@@ -42,14 +41,15 @@ class AssemblyBase : public SubsystemBase {
static const ReturnValue_t NEED_TO_CHANGE_HEALTH = MAKE_RETURN_CODE(0x05);
static const ReturnValue_t NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE = MAKE_RETURN_CODE(0xa1);
- AssemblyBase(object_id_t objectId, object_id_t parentId, uint16_t commandQueueDepth = 8);
+ AssemblyBase(object_id_t objectId, uint16_t commandQueueDepth = 8);
virtual ~AssemblyBase();
protected:
/**
- * Command children to reach [mode,submode] combination
- * Can be done by setting #commandsOutstanding correctly,
- * or using executeTable()
+ * Command children to reach [mode,submode] combination. Can be done by setting
+ * #commandsOutstanding correctly, or using #executeTable. In case of an FDIR recovery,
+ * the user needs to ensure that the target devices are healthy. If a device is not healthy,
+ * a recovery might be on-going and the device needs to be commanded to off first.
* @param mode
* @param submode
* @return
@@ -120,8 +120,19 @@ class AssemblyBase : public SubsystemBase {
virtual ReturnValue_t handleHealthReply(CommandMessage *message);
- virtual void performChildOperation();
+ /**
+ * @brief Default periodic handler
+ * @details
+ * This is the default periodic handler which will be called by the SubsystemBase
+ * performOperation. It performs the child transitions or reacts to changed health/mode states
+ * of children objects
+ */
+ virtual void performChildOperation() override;
+ /**
+ * This function handles changed mode or health states of children
+ * @return
+ */
bool handleChildrenChanged();
/**
@@ -134,12 +145,37 @@ class AssemblyBase : public SubsystemBase {
bool handleChildrenChangedHealth();
+ /**
+ * Core transition handler. The default implementation will only do something if
+ * #commandsOutstanding is smaller or equal to zero, which means that all mode commands
+ * from the #doPerformTransition call were executed successfully.
+ *
+ * Unless a second step was requested, the function will then use #checkChildrenState to
+ * determine whether the target mode was reached.
+ *
+ * There is some special handling for certain (internal) modes:
+ * - A second step is necessary. #commandChildren will be performed again
+ * - The device health was overwritten. #commandChildren will be called
+ * - A recovery is ongoing. #checkAndHandleRecovery will be called.
+ */
virtual void handleChildrenTransition();
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, uint32_t *msToReachTheMode);
+ /**
+ * Calls #doStartTransition and triggers an informative event as well that the mode will
+ * change
+ * @param mode
+ * @param submode
+ */
virtual void startTransition(Mode_t mode, Submode_t submode);
+ /**
+ * This function starts the transition by setting the internal #targetSubmode and #targetMode
+ * variables and then calling the #commandChildren function.
+ * @param mode
+ * @param submode
+ */
virtual void doStartTransition(Mode_t mode, Submode_t submode);
virtual bool isInTransition();
@@ -160,7 +196,7 @@ class AssemblyBase : public SubsystemBase {
* Manages recovery of a device
* @return true if recovery is still ongoing, false else.
*/
- bool checkAndHandleRecovery();
+ virtual bool checkAndHandleRecovery();
/**
* Helper method to overwrite health state of one of the children.
@@ -168,6 +204,8 @@ class AssemblyBase : public SubsystemBase {
* @param objectId Must be a registered child.
*/
void overwriteDeviceHealth(object_id_t objectId, HasHealthIF::HealthState oldHealth);
+
+ void triggerModeHelperEvents(Mode_t mode, Submode_t submode);
};
#endif /* FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ */
diff --git a/src/fsfw/devicehandlers/ChildHandlerBase.cpp b/src/fsfw/devicehandlers/ChildHandlerBase.cpp
index ecd4cfc8d..37af39dc5 100644
--- a/src/fsfw/devicehandlers/ChildHandlerBase.cpp
+++ b/src/fsfw/devicehandlers/ChildHandlerBase.cpp
@@ -3,17 +3,12 @@
#include "fsfw/subsystem/SubsystemBase.h"
ChildHandlerBase::ChildHandlerBase(object_id_t setObjectId, object_id_t deviceCommunication,
- CookieIF* cookie, object_id_t hkDestination,
- uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId,
- object_id_t parent, FailureIsolationBase* customFdir,
- size_t cmdQueueSize)
+ CookieIF* cookie, HasModeTreeChildrenIF& parent,
+ FailureIsolationBase* customFdir, size_t cmdQueueSize)
: DeviceHandlerBase(setObjectId, deviceCommunication, cookie,
(customFdir == nullptr ? &childHandlerFdir : customFdir), cmdQueueSize),
- parentId(parent),
- childHandlerFdir(setObjectId) {
- this->setHkDestination(hkDestination);
- this->setThermalStateRequestPoolIds(thermalStatePoolId, thermalRequestPoolId);
-}
+ parent(parent),
+ childHandlerFdir(setObjectId) {}
ChildHandlerBase::~ChildHandlerBase() {}
@@ -23,21 +18,5 @@ ReturnValue_t ChildHandlerBase::initialize() {
return result;
}
- MessageQueueId_t parentQueue = 0;
-
- if (parentId != objects::NO_OBJECT) {
- SubsystemBase* parent = ObjectManager::instance()->get(parentId);
- if (parent == NULL) {
- return returnvalue::FAILED;
- }
- parentQueue = parent->getCommandQueue();
-
- parent->registerChild(getObjectId());
- }
-
- healthHelper.setParentQueue(parentQueue);
-
- modeHelper.setParentQueue(parentQueue);
-
- return returnvalue::OK;
+ return DeviceHandlerBase::connectModeTreeParent(parent);
}
diff --git a/src/fsfw/devicehandlers/ChildHandlerBase.h b/src/fsfw/devicehandlers/ChildHandlerBase.h
index 19d48a24a..c98baf3de 100644
--- a/src/fsfw/devicehandlers/ChildHandlerBase.h
+++ b/src/fsfw/devicehandlers/ChildHandlerBase.h
@@ -1,22 +1,23 @@
#ifndef FSFW_DEVICEHANDLER_CHILDHANDLERBASE_H_
#define FSFW_DEVICEHANDLER_CHILDHANDLERBASE_H_
+#include
+
#include "ChildHandlerFDIR.h"
#include "DeviceHandlerBase.h"
class ChildHandlerBase : public DeviceHandlerBase {
public:
ChildHandlerBase(object_id_t setObjectId, object_id_t deviceCommunication, CookieIF* cookie,
- object_id_t hkDestination, uint32_t thermalStatePoolId,
- uint32_t thermalRequestPoolId, object_id_t parent = objects::NO_OBJECT,
- FailureIsolationBase* customFdir = nullptr, size_t cmdQueueSize = 20);
+ HasModeTreeChildrenIF& parent, FailureIsolationBase* customFdir = nullptr,
+ size_t cmdQueueSize = 20);
virtual ~ChildHandlerBase();
virtual ReturnValue_t initialize();
protected:
- const uint32_t parentId;
+ HasModeTreeChildrenIF& parent;
ChildHandlerFDIR childHandlerFdir;
};
diff --git a/src/fsfw/devicehandlers/DeviceCommunicationIF.h b/src/fsfw/devicehandlers/DeviceCommunicationIF.h
index a5546a36a..cf8272407 100644
--- a/src/fsfw/devicehandlers/DeviceCommunicationIF.h
+++ b/src/fsfw/devicehandlers/DeviceCommunicationIF.h
@@ -49,6 +49,7 @@ class DeviceCommunicationIF {
// is this needed if there is no open/close call?
static const ReturnValue_t NOT_ACTIVE = MAKE_RETURN_CODE(0x05);
static const ReturnValue_t TOO_MUCH_DATA = MAKE_RETURN_CODE(0x06);
+ static constexpr ReturnValue_t BUSY = MAKE_RETURN_CODE(0x07);
virtual ~DeviceCommunicationIF() {}
diff --git a/src/fsfw/devicehandlers/DeviceHandlerBase.cpp b/src/fsfw/devicehandlers/DeviceHandlerBase.cpp
index bc528128f..00d6c4519 100644
--- a/src/fsfw/devicehandlers/DeviceHandlerBase.cpp
+++ b/src/fsfw/devicehandlers/DeviceHandlerBase.cpp
@@ -1,4 +1,4 @@
-#include "fsfw/devicehandlers/DeviceHandlerBase.h"
+#include "DeviceHandlerBase.h"
#include "fsfw/datapool/PoolReadGuard.h"
#include "fsfw/datapoollocal/LocalPoolVariable.h"
@@ -13,6 +13,7 @@
#include "fsfw/serviceinterface/ServiceInterface.h"
#include "fsfw/storagemanager/StorageManagerIF.h"
#include "fsfw/subsystem/SubsystemBase.h"
+#include "fsfw/subsystem/helper.h"
#include "fsfw/thermal/ThermalComponentIF.h"
object_id_t DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT;
@@ -23,8 +24,6 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, object_id_t device
CookieIF* comCookie, FailureIsolationBase* fdirInstance,
size_t cmdQueueSize)
: SystemObject(setObjectId),
- mode(MODE_OFF),
- submode(SUBMODE_NONE),
wiretappingMode(OFF),
storedRawData(StorageManagerIF::INVALID_ADDRESS),
deviceCommunicationId(deviceCommunication),
@@ -39,10 +38,13 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, object_id_t device
defaultFDIRUsed(fdirInstance == nullptr),
switchOffWasReported(false),
childTransitionDelay(5000),
+ mode(MODE_OFF),
+ submode(SUBMODE_NONE),
transitionSourceMode(_MODE_POWER_DOWN),
transitionSourceSubMode(SUBMODE_NONE) {
+ auto mqArgs = MqArgs(setObjectId, static_cast(this));
commandQueue = QueueFactory::instance()->createMessageQueue(
- cmdQueueSize, MessageQueueMessage::MAX_MESSAGE_SIZE);
+ cmdQueueSize, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs);
insertInCommandMap(RAW_COMMAND_ID);
cookieInfo.state = COOKIE_UNUSED;
cookieInfo.pendingCommand = deviceCommandMap.end();
@@ -50,21 +52,13 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, object_id_t device
printWarningOrError(sif::OutputTypes::OUT_ERROR, "DeviceHandlerBase", returnvalue::FAILED,
"Invalid cookie");
}
- if (this->fdirInstance == nullptr) {
- this->fdirInstance = new DeviceHandlerFailureIsolation(setObjectId, defaultFdirParentId);
- }
}
void DeviceHandlerBase::setHkDestination(object_id_t hkDestination) {
this->hkDestination = hkDestination;
}
-void DeviceHandlerBase::setThermalStateRequestPoolIds(lp_id_t thermalStatePoolId,
- lp_id_t heaterRequestPoolId,
- uint32_t thermalSetId) {
- thermalSet =
- new DeviceHandlerThermalSet(this, thermalSetId, thermalStatePoolId, heaterRequestPoolId);
-}
+void DeviceHandlerBase::enableThermalModule(ThermalStateCfg cfg) { this->thermalStateCfg = cfg; }
DeviceHandlerBase::~DeviceHandlerBase() {
if (comCookie != nullptr) {
@@ -130,6 +124,10 @@ ReturnValue_t DeviceHandlerBase::initialize() {
if (result != returnvalue::OK) {
return result;
}
+ if (this->fdirInstance == nullptr) {
+ this->fdirInstance =
+ new DeviceHandlerFailureIsolation(this->getObjectId(), defaultFdirParentId);
+ }
communicationInterface =
ObjectManager::instance()->get(deviceCommunicationId);
@@ -224,12 +222,11 @@ ReturnValue_t DeviceHandlerBase::initialize() {
fillCommandAndReplyMap();
if (thermalSet != nullptr) {
+ PoolReadGuard pg(thermalSet);
// Set temperature target state to NON_OP.
- result = thermalSet->read();
- if (result == returnvalue::OK) {
+ if (pg.getReadResult() == returnvalue::OK) {
thermalSet->heaterRequest.value = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL;
thermalSet->heaterRequest.setValid(true);
- thermalSet->commit();
}
}
@@ -353,7 +350,6 @@ void DeviceHandlerBase::doStateMachine() {
currentUptime - timeoutStart >= powerSwitcher->getSwitchDelayMs()) {
triggerEvent(MODE_TRANSITION_FAILED, PowerSwitchIF::SWITCH_TIMEOUT, 0);
setMode(_MODE_POWER_DOWN);
- callChildStatemachine();
break;
}
ReturnValue_t switchState = getStateOfSwitches();
@@ -367,13 +363,12 @@ void DeviceHandlerBase::doStateMachine() {
}
} break;
case _MODE_WAIT_OFF: {
- uint32_t currentUptime;
- Clock::getUptime(¤tUptime);
-
if (powerSwitcher == nullptr) {
setMode(MODE_OFF);
break;
}
+ uint32_t currentUptime;
+ Clock::getUptime(¤tUptime);
if (currentUptime - timeoutStart >= powerSwitcher->getSwitchDelayMs()) {
triggerEvent(MODE_TRANSITION_FAILED, PowerSwitchIF::SWITCH_TIMEOUT, 0);
setMode(MODE_ERROR_ON);
@@ -381,7 +376,7 @@ void DeviceHandlerBase::doStateMachine() {
}
ReturnValue_t switchState = getStateOfSwitches();
if ((switchState == PowerSwitchIF::SWITCH_OFF) || (switchState == NO_SWITCH)) {
- setMode(_MODE_SWITCH_IS_OFF);
+ setMode(MODE_OFF, SUBMODE_NONE);
}
} break;
case MODE_OFF:
@@ -394,9 +389,6 @@ void DeviceHandlerBase::doStateMachine() {
case MODE_NORMAL:
case MODE_ERROR_ON:
break;
- case _MODE_SWITCH_IS_OFF:
- setMode(MODE_OFF, SUBMODE_NONE);
- break;
default:
triggerEvent(OBJECT_IN_INVALID_MODE, mode, submode);
setMode(_MODE_POWER_DOWN, 0);
@@ -568,25 +560,40 @@ void DeviceHandlerBase::setTransition(Mode_t modeTo, Submode_t submodeTo) {
}
void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) {
- /* TODO: This will probably be done by the LocalDataPoolManager now */
- // changeHK(mode, submode, false);
+ /**
+ * handle transition from OFF to NORMAL by continuing towards normal when ON is reached
+ */
+ if (newMode == MODE_ON and continueToNormal) {
+ continueToNormal = false;
+ // TODO: Check whether the following two lines are okay to do so.
+ transitionSourceMode = MODE_ON;
+ transitionSourceSubMode = newSubmode;
+ mode = _MODE_TO_NORMAL;
+ return;
+ }
+
submode = newSubmode;
mode = newMode;
modeChanged();
setNormalDatapoolEntriesInvalid();
+ if (newMode == MODE_OFF) {
+ disableCommandsAndReplies();
+ }
if (!isTransitionalMode()) {
+ // clear this flag when a non-transitional Mode is reached to be safe
+ continueToNormal = false;
modeHelper.modeChanged(newMode, newSubmode);
announceMode(false);
}
Clock::getUptime(&timeoutStart);
if (mode == MODE_OFF and thermalSet != nullptr) {
- ReturnValue_t result = thermalSet->read();
- if (result == returnvalue::OK) {
+ PoolReadGuard pg(thermalSet);
+ if (pg.getReadResult() == returnvalue::OK) {
if (thermalSet->heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) {
thermalSet->heaterRequest.value = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL;
}
- thermalSet->heaterRequest.commit(PoolVariableIF::VALID);
+ thermalSet->heaterRequest.setValid(true);
}
}
/* TODO: This will probably be done by the LocalDataPoolManager now */
@@ -1059,8 +1066,7 @@ Mode_t DeviceHandlerBase::getBaseMode(Mode_t transitionMode) {
return transitionMode & ~(TRANSITION_MODE_BASE_ACTION_MASK | TRANSITION_MODE_CHILD_ACTION_MASK);
}
-// SHOULDDO: Allow transition from OFF to NORMAL to reduce complexity in assemblies. And, by the
-// way, throw away DHB and write a new one:
+// SHOULDDO: throw away DHB and write a new one:
// - Include power and thermal completely, but more modular :-)
// - Don't use modes for state transitions, reduce FSM (Finte State Machine) complexity.
// - Modularization?
@@ -1072,13 +1078,12 @@ ReturnValue_t DeviceHandlerBase::checkModeCommand(Mode_t commandedMode, Submode_
if ((mode == MODE_ERROR_ON) && (commandedMode != MODE_OFF)) {
return TRANS_NOT_ALLOWED;
}
- if ((commandedMode == MODE_NORMAL) && (mode == MODE_OFF)) {
- return TRANS_NOT_ALLOWED;
- }
- if ((commandedMode == MODE_ON) && (mode == MODE_OFF) and (thermalSet != nullptr)) {
- ReturnValue_t result = thermalSet->read();
- if (result == returnvalue::OK) {
+ // Do not check thermal state for MODE_RAW
+ if ((mode == MODE_OFF) and ((commandedMode == MODE_ON) or (commandedMode == MODE_NORMAL)) and
+ (thermalSet != nullptr)) {
+ PoolReadGuard pg(thermalSet);
+ if (pg.getReadResult() == returnvalue::OK) {
if ((thermalSet->heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) and
(not ThermalComponentIF::isOperational(thermalSet->thermalState.value))) {
triggerEvent(ThermalComponentIF::TEMP_NOT_IN_OP_RANGE, thermalSet->thermalState.value);
@@ -1091,6 +1096,7 @@ ReturnValue_t DeviceHandlerBase::checkModeCommand(Mode_t commandedMode, Submode_
}
void DeviceHandlerBase::startTransition(Mode_t commandedMode, Submode_t commandedSubmode) {
+ continueToNormal = false;
switch (commandedMode) {
case MODE_ON:
handleTransitionToOnMode(commandedMode, commandedSubmode);
@@ -1120,8 +1126,9 @@ void DeviceHandlerBase::startTransition(Mode_t commandedMode, Submode_t commande
case MODE_NORMAL:
if (mode != MODE_OFF) {
setTransition(MODE_NORMAL, commandedSubmode);
- } else {
- replyReturnvalueToCommand(HasModesIF::TRANS_NOT_ALLOWED);
+ } else { // mode is off
+ continueToNormal = true;
+ handleTransitionToOnMode(MODE_NORMAL, commandedSubmode);
}
break;
}
@@ -1137,11 +1144,10 @@ void DeviceHandlerBase::handleTransitionToOnMode(Mode_t commandedMode, Submode_t
childTransitionDelay = getTransitionDelayMs(_MODE_START_UP, MODE_ON);
triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode);
if (thermalSet != nullptr) {
- ReturnValue_t result = thermalSet->read();
- if (result == returnvalue::OK) {
+ PoolReadGuard pg(thermalSet);
+ if (pg.getReadResult() == returnvalue::OK) {
if (thermalSet->heaterRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) {
thermalSet->heaterRequest = ThermalComponentIF::STATE_REQUEST_OPERATIONAL;
- thermalSet->commit();
}
}
}
@@ -1279,6 +1285,7 @@ void DeviceHandlerBase::handleDeviceTm(const SerializeIF& dataSet, DeviceCommand
if (iter->second.command != deviceCommandMap.end()) {
MessageQueueId_t queueId = iter->second.command->second.sendReplyTo;
+ // This may fail, but we'll ignore the fault.
if (queueId != NO_COMMANDER) {
// This may fail, but we'll ignore the fault.
actionHelper.reportData(queueId, replyId, const_cast(&dataSet));
@@ -1457,15 +1464,17 @@ void DeviceHandlerBase::setTaskIF(PeriodicTaskIF* task) { executingTask = task;
void DeviceHandlerBase::debugInterface(uint8_t positionTracker, object_id_t objectId,
uint32_t parameter) {}
+Submode_t DeviceHandlerBase::getInitialSubmode() { return SUBMODE_NONE; }
+
void DeviceHandlerBase::performOperationHook() {}
ReturnValue_t DeviceHandlerBase::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) {
- if (thermalSet != nullptr) {
- localDataPoolMap.emplace(thermalSet->thermalStatePoolId,
- new PoolEntry);
- localDataPoolMap.emplace(thermalSet->heaterRequestPoolId,
- new PoolEntry);
+ if (thermalStateCfg.has_value()) {
+ localDataPoolMap.emplace(thermalStateCfg.value().thermalStatePoolId,
+ new PoolEntry());
+ localDataPoolMap.emplace(thermalStateCfg.value().thermalRequestPoolId,
+ new PoolEntry());
}
return returnvalue::OK;
}
@@ -1478,8 +1487,12 @@ ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() {
}
this->poolManager.initializeAfterTaskCreation();
+ if (thermalStateCfg.has_value()) {
+ ThermalStateCfg& cfg = thermalStateCfg.value();
+ thermalSet = new DeviceHandlerThermalSet(this, cfg);
+ }
if (setStartupImmediately) {
- startTransition(MODE_ON, SUBMODE_NONE);
+ startTransition(MODE_ON, getInitialSubmode());
}
return returnvalue::OK;
}
@@ -1566,3 +1579,52 @@ MessageQueueId_t DeviceHandlerBase::getCommanderQueueId(DeviceCommandId_t replyI
}
return commandIter->second.sendReplyTo;
}
+
+void DeviceHandlerBase::setCustomFdir(FailureIsolationBase* fdir) { this->fdirInstance = fdir; }
+
+void DeviceHandlerBase::setPowerSwitcher(PowerSwitchIF* switcher) {
+ this->powerSwitcher = switcher;
+}
+
+Mode_t DeviceHandlerBase::getMode() { return mode; }
+
+Submode_t DeviceHandlerBase::getSubmode() { return submode; }
+
+void DeviceHandlerBase::disableCommandsAndReplies() {
+ for (auto& command : deviceCommandMap) {
+ if (command.second.isExecuting) {
+ command.second.isExecuting = false;
+ }
+ }
+ for (auto& reply : deviceReplyMap) {
+ if (!reply.second.periodic) {
+ if (reply.second.countdown != nullptr) {
+ reply.second.countdown->timeOut();
+ } else {
+ reply.second.delayCycles = 0;
+ }
+ reply.second.active = false;
+ }
+ }
+}
+
+ReturnValue_t DeviceHandlerBase::connectModeTreeParent(HasModeTreeChildrenIF& parent) {
+ return modetree::connectModeTreeParent(parent, *this, &healthHelper, modeHelper);
+}
+
+const HasHealthIF* DeviceHandlerBase::getOptHealthIF() const { return this; }
+
+const HasModesIF& DeviceHandlerBase::getModeIF() const { return *this; }
+
+ModeTreeChildIF& DeviceHandlerBase::getModeTreeChildIF() { return *this; }
+
+ReturnValue_t DeviceHandlerBase::finishAction(bool success, DeviceCommandId_t action,
+ ReturnValue_t result) {
+ auto commandIter = deviceCommandMap.find(action);
+ if (commandIter == deviceCommandMap.end()) {
+ return MessageQueueIF::NO_QUEUE;
+ }
+ commandIter->second.isExecuting = false;
+ actionHelper.finish(success, commandIter->second.sendReplyTo, action, result);
+ return returnvalue::OK;
+}
diff --git a/src/fsfw/devicehandlers/DeviceHandlerBase.h b/src/fsfw/devicehandlers/DeviceHandlerBase.h
index 700e960d1..08298bdc4 100644
--- a/src/fsfw/devicehandlers/DeviceHandlerBase.h
+++ b/src/fsfw/devicehandlers/DeviceHandlerBase.h
@@ -2,6 +2,7 @@
#define FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_
#include