Compare commits
95
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc9c98cf8e | ||
|
|
03d3b6cd68 | ||
|
|
6faea2b0f0 | ||
|
|
844faf850f | ||
|
|
e256fa4b92 | ||
|
|
9d609b6d2d | ||
|
|
892fdff164 | ||
|
|
30965ed058 | ||
|
|
fa7ecca728 | ||
|
|
0224523cc5 | ||
|
|
42ecc7caf1 | ||
|
|
17926777a1 | ||
|
|
5761c1e187 | ||
|
|
427f5a99b9 | ||
|
|
b1d2a4726f | ||
|
|
4a47eced59 | ||
|
|
b123b3f260 | ||
|
|
428ff3f373 | ||
|
|
9890a2c52e | ||
|
|
b65854eaa0 | ||
|
|
23500c8364 | ||
|
|
91c5b05723 | ||
|
|
a8bcb9c8cd | ||
|
|
1d278d6f5c | ||
|
|
b1bc699009 | ||
|
|
3668e61d5c | ||
|
|
45150c8ce3 | ||
|
|
7692e598d6 | ||
|
|
52129e0c84 | ||
|
|
acf60e55e8 | ||
|
|
a625a06b7d | ||
|
|
c0a665ffe6 | ||
|
|
cceef62cb6 | ||
|
|
4c3c93c106 | ||
|
|
d28e2b5f07 | ||
|
|
6ebe3123ff | ||
|
|
70b9ba68bf
|
||
|
|
59706365f6 | ||
|
|
0c70ff1822 | ||
|
|
76dd1d1562 | ||
|
|
f2b72db481 | ||
|
|
fa4af546fa | ||
|
|
5745d7f01c | ||
|
|
d7c1d05599 | ||
|
|
86b83810c3 | ||
|
|
d0904fdaa2 | ||
|
|
f824c066d1 | ||
|
|
d000365b99 | ||
|
|
d99f6fd356 | ||
|
|
49eaeae42b | ||
|
|
7bfc536cf6 | ||
|
|
1da7f7f122 | ||
|
|
aa443e6aa6 | ||
|
|
b13b5b456d | ||
|
|
297ec261ce | ||
|
|
95520d7d0c | ||
|
|
b665b2effe | ||
|
|
7ae58f8125 | ||
|
|
7784a26a10 | ||
|
|
3afd0c8d3c | ||
|
|
71623d5314 | ||
|
|
daac5ea727 | ||
|
|
2c01b83b75 | ||
|
|
f01e58a757 | ||
|
|
40be8ebef5 | ||
|
|
2af6e85f87 | ||
|
|
d8ac312e85 | ||
|
|
1e12753533 | ||
|
|
b7699b327b | ||
|
|
9945f72eaf | ||
|
|
55b8d01b93 | ||
|
|
8cb1d84c58 | ||
|
|
8801dfa31d | ||
|
|
b8979d8f90 | ||
|
|
9557db7036 | ||
|
|
c9fcabccd6 | ||
|
|
ecd36f5e52 | ||
|
|
fc19c0838e | ||
|
|
8c3f366d1a | ||
|
|
6efb2641a7 | ||
|
|
260bbad9a0 | ||
|
|
9edd6221f8 | ||
|
|
735e341aab | ||
|
|
921bfb1e99 | ||
|
|
5b1651e1a6 | ||
|
|
e916b9b096 | ||
|
|
b14e761bad | ||
|
|
33f3ae2434 | ||
|
|
f0087d5b0d | ||
|
|
69c33587e8 | ||
|
|
d1bf04cc29 | ||
|
|
8e3bc1b8aa | ||
|
|
64f97fc3ba | ||
|
|
1427fbd2fe | ||
|
|
81cd8bd290 |
No files matched your search
+19
-1
@@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Added
|
||||
|
||||
- functions to configure pus routing
|
||||
- FreeRTOS monotonic clock which is not subjected to time jumps of the system clock
|
||||
- add CFDP subsystem ID
|
||||
https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/742
|
||||
- `PusTmZcWriter` now exposes API to set message counter field.
|
||||
@@ -34,6 +36,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Changed
|
||||
|
||||
- send HK one-parameter-report back to sender instead of default hk queue
|
||||
- Complete overhaul of HK subsystem. Replaced local data pool manager by periodic HK
|
||||
helper. The shared pool and the periodic HK generation are now distinct concepts.
|
||||
- The local HK manager was replaced by a periodic HK helper which has reduced responsibilities.
|
||||
It takes care of tracking the HK generation using a set specification provided by the user.n
|
||||
However, it leaves serialization of the HK data completely to the developer. This removes a major
|
||||
constraint on the format of the HK data, which was previously constrained to implementors of a
|
||||
certain base class.
|
||||
- The former set classes and pool objects are still available for HK set specification and
|
||||
generation. The API has changed, but the general usage and their architecture has not.
|
||||
- A new set of set classes and helper objects to specify HK sets and data which does not need to be
|
||||
shared was added as well. The majority of datasets do not need to be shared anyway.
|
||||
- The non-shared API retain the capability of appending of a validity blob for each piece of set
|
||||
data at the end of the HK data. For both non-shared and shared data, this capability can be
|
||||
specified in the constructor, and defaults to true.
|
||||
- Improved File System Abstraction to be more in line with normal filesystems.
|
||||
- CFDP implementation was improved, has now even less dependencies on other FSFW components
|
||||
and allows one inserted packet per state machine call.
|
||||
@@ -53,7 +70,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
configurable.
|
||||
- Switched to vendored versions for both the Embedded Template Library (ETL) and the
|
||||
Catch2 unittesting library.
|
||||
|
||||
- Increased maximum number of mode tables from 70 to 100
|
||||
- Exposed health table mutex via getter function
|
||||
## Added
|
||||
|
||||
- `EventManager`: Add function to print all listeners.
|
||||
|
||||
+14
-10
@@ -8,7 +8,8 @@ GyroHandlerL3GD20H::GyroHandlerL3GD20H(object_id_t objectId, object_id_t deviceC
|
||||
CookieIF *comCookie, uint32_t transitionDelayMs)
|
||||
: DeviceHandlerBase(objectId, deviceCommunication, comCookie),
|
||||
transitionDelayMs(transitionDelayMs),
|
||||
dataset(this) {}
|
||||
sharedPool(DeviceHandlerBase::getObjectId()),
|
||||
dataset(sharedPool) {}
|
||||
|
||||
GyroHandlerL3GD20H::~GyroHandlerL3GD20H() {}
|
||||
|
||||
@@ -210,27 +211,27 @@ ReturnValue_t GyroHandlerL3GD20H::interpretDeviceReply(DeviceCommandId_t id,
|
||||
if (readSet.getReadResult() == returnvalue::OK) {
|
||||
if (std::abs(angVelocX) < this->absLimitX) {
|
||||
dataset.angVelocX = angVelocX;
|
||||
dataset.angVelocX.setValid(true);
|
||||
// dataset.angVelocX.setValid(true);
|
||||
} else {
|
||||
dataset.angVelocX.setValid(false);
|
||||
// dataset.angVelocX.setValid(false);
|
||||
}
|
||||
|
||||
if (std::abs(angVelocY) < this->absLimitY) {
|
||||
dataset.angVelocY = angVelocY;
|
||||
dataset.angVelocY.setValid(true);
|
||||
// dataset.angVelocY.setValid(true);
|
||||
} else {
|
||||
dataset.angVelocY.setValid(false);
|
||||
// dataset.angVelocY.setValid(false);
|
||||
}
|
||||
|
||||
if (std::abs(angVelocZ) < this->absLimitZ) {
|
||||
dataset.angVelocZ = angVelocZ;
|
||||
dataset.angVelocZ.setValid(true);
|
||||
// dataset.angVelocZ.setValid(true);
|
||||
} else {
|
||||
dataset.angVelocZ.setValid(false);
|
||||
// dataset.angVelocZ.setValid(false);
|
||||
}
|
||||
|
||||
dataset.temperature = temperature;
|
||||
dataset.temperature.setValid(true);
|
||||
// dataset.temperature.setValid(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -246,16 +247,19 @@ uint32_t GyroHandlerL3GD20H::getTransitionDelayMs(Mode_t from, Mode_t to) {
|
||||
|
||||
void GyroHandlerL3GD20H::setToGoToNormalMode(bool enable) { this->goNormalModeImmediately = true; }
|
||||
|
||||
// TODO
|
||||
/*
|
||||
ReturnValue_t GyroHandlerL3GD20H::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) {
|
||||
PeriodicHkGenerationHelper &hkGenHelper) {
|
||||
localDataPoolMap.emplace(l3gd20h::ANG_VELOC_X, new PoolEntry<float>({0.0}));
|
||||
localDataPoolMap.emplace(l3gd20h::ANG_VELOC_Y, new PoolEntry<float>({0.0}));
|
||||
localDataPoolMap.emplace(l3gd20h::ANG_VELOC_Z, new PoolEntry<float>({0.0}));
|
||||
localDataPoolMap.emplace(l3gd20h::TEMPERATURE, new PoolEntry<float>({0.0}));
|
||||
poolManager.subscribeForRegularPeriodicPacket(
|
||||
hkGenHelper.enableRegularPeriodicPacket(
|
||||
subdp::RegularHkPeriodicParams(dataset.getSid(), false, 10.0));
|
||||
return returnvalue::OK;
|
||||
}
|
||||
*/
|
||||
|
||||
void GyroHandlerL3GD20H::fillCommandAndReplyMap() {
|
||||
insertInCommandAndReplyMap(l3gd20h::READ_REGS, 1, &dataset);
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef MISSION_DEVICES_GYROL3GD20HANDLER_H_
|
||||
#define MISSION_DEVICES_GYROL3GD20HANDLER_H_
|
||||
|
||||
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
|
||||
#include <fsfw/globalfunctions/PeriodicOperationDivider.h>
|
||||
#include <fsfw_hal/devicehandlers/devicedefinitions/gyroL3gHelpers.h>
|
||||
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
|
||||
#include "fsfw/globalfunctions/PeriodicOperationDivider.h"
|
||||
#include "gyroL3gHelpers.h"
|
||||
|
||||
/**
|
||||
* @brief Device Handler for the L3GD20H gyroscope sensor
|
||||
@@ -51,11 +51,12 @@ class GyroHandlerL3GD20H : public DeviceHandlerBase {
|
||||
void fillCommandAndReplyMap() override;
|
||||
void modeChanged() override;
|
||||
virtual uint32_t getTransitionDelayMs(Mode_t from, Mode_t to) override;
|
||||
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) override;
|
||||
// ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
// PeriodicHkGenerationHelper &hkGenHelper) override;
|
||||
|
||||
private:
|
||||
uint32_t transitionDelayMs = 0;
|
||||
localpool::SharedPool sharedPool;
|
||||
GyroPrimaryDataset dataset;
|
||||
|
||||
float absLimitX = l3gd20h::RANGE_DPS_00;
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "HasLocalDpIFManagerAttorney.h"
|
||||
|
||||
#include "fsfw/datapool/LocalPoolObjectBase.h"
|
||||
#include "fsfw/housekeeping/GeneratesPeriodicHkIF.h"
|
||||
|
||||
LocalPoolObjectBase* HasLocalDpIFManagerAttorney::getPoolObjectHandle(
|
||||
PeriodicHkGenerationIF* clientIF, dp::lp_id_t localPoolId) {
|
||||
return clientIF->getPoolObjectHandle(localPoolId);
|
||||
}
|
||||
|
||||
object_id_t HasLocalDpIFManagerAttorney::getObjectId(PeriodicHkGenerationIF* clientIF) {
|
||||
return clientIF->getObjectId();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <fsfw/housekeeping/PeriodicHkHelper.h>
|
||||
|
||||
#include "fsfw/datapool/definitions.h"
|
||||
|
||||
class PeriodicHkGenerationIF;
|
||||
class LocalPoolDataSetBase;
|
||||
class LocalPoolObjectBase;
|
||||
|
||||
class HasLocalDpIFManagerAttorney {
|
||||
static LocalPoolObjectBase* getPoolObjectHandle(PeriodicHkGenerationIF* clientIF,
|
||||
dp::id_t localPoolId);
|
||||
|
||||
static object_id_t getObjectId(PeriodicHkGenerationIF* clientIF);
|
||||
|
||||
friend class hk::PeriodicHelper;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
#include "HasLocalDpIFUserAttorney.h"
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDPIFUSERATTORNEY_H_
|
||||
#define FSFW_DATAPOOLLOCAL_HASLOCALDPIFUSERATTORNEY_H_
|
||||
|
||||
class HasLocalDataPoolIF;
|
||||
class PeriodicHkGenerationIF;
|
||||
class AccessPoolManagerIF;
|
||||
|
||||
class HasLocalDpIFUserAttorney {
|
||||
private:
|
||||
static AccessPoolManagerIF* getAccessorHandle(HasLocalDataPoolIF* clientIF);
|
||||
// static AccessPoolManagerIF* getAccessorHandle(PeriodicHkGenerationIF* clientIF);
|
||||
|
||||
friend class LocalPoolObjectBase;
|
||||
friend class LocalPoolDataSetBase;
|
||||
+6
-4
@@ -273,7 +273,7 @@ ReturnValue_t MgmLIS3MDLHandler::interpretDeviceReply(DeviceCommandId_t id, cons
|
||||
if (readHelper.getReadResult() == returnvalue::OK) {
|
||||
if (std::abs(mgmX) > absLimitX or std::abs(mgmY) > absLimitY or
|
||||
std::abs(mgmZ) > absLimitZ) {
|
||||
dataset.fieldStrengths.setValid(false);
|
||||
dataset.setIsValid = false;
|
||||
}
|
||||
if (std::abs(mgmX) < absLimitX) {
|
||||
dataset.fieldStrengths[0] = mgmX;
|
||||
@@ -286,7 +286,7 @@ ReturnValue_t MgmLIS3MDLHandler::interpretDeviceReply(DeviceCommandId_t id, cons
|
||||
if (std::abs(mgmZ) < absLimitZ) {
|
||||
dataset.fieldStrengths[2] = mgmZ;
|
||||
}
|
||||
dataset.fieldStrengths.setValid(true);
|
||||
dataset.setIsValid = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -415,13 +415,15 @@ uint32_t MgmLIS3MDLHandler::getTransitionDelayMs(Mode_t from, Mode_t to) { retur
|
||||
|
||||
void MgmLIS3MDLHandler::modeChanged(void) { internalState = InternalState::STATE_NONE; }
|
||||
|
||||
/*
|
||||
ReturnValue_t MgmLIS3MDLHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) {
|
||||
PeriodicHkGenerationHelper &poolManager) {
|
||||
localDataPoolMap.emplace(mgmLis3::FIELD_STRENGTHS, &mgmXYZ);
|
||||
localDataPoolMap.emplace(mgmLis3::TEMPERATURE_CELCIUS, &temperature);
|
||||
poolManager.subscribeForRegularPeriodicPacket({dataset.getSid(), false, 10.0});
|
||||
poolManager.setPeriodicFrequency(dataset.getSid(), 10'000);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
*/
|
||||
|
||||
void MgmLIS3MDLHandler::setAbsoluteLimits(float xLimit, float yLimit, float zLimit) {
|
||||
this->absLimitX = xLimit;
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef MISSION_DEVICES_MGMLIS3MDLHANDLER_H_
|
||||
#define MISSION_DEVICES_MGMLIS3MDLHANDLER_H_
|
||||
|
||||
#include <fsfw_hal/devicehandlers/devicedefinitions/mgmLis3Helpers.h>
|
||||
#include "mgmLis3Helpers.h"
|
||||
|
||||
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
|
||||
#include "fsfw/globalfunctions/PeriodicOperationDivider.h"
|
||||
@@ -63,8 +63,8 @@ class MgmLIS3MDLHandler : public DeviceHandlerBase {
|
||||
virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) override;
|
||||
void fillCommandAndReplyMap() override;
|
||||
void modeChanged(void) override;
|
||||
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) override;
|
||||
// ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
// PeriodicHkGenerationHelper &poolManager) override;
|
||||
|
||||
private:
|
||||
mgmLis3::MgmPrimaryDataset dataset;
|
||||
+7
-4
@@ -9,7 +9,7 @@
|
||||
MgmRM3100Handler::MgmRM3100Handler(object_id_t objectId, object_id_t deviceCommunication,
|
||||
CookieIF *comCookie, uint32_t transitionDelay)
|
||||
: DeviceHandlerBase(objectId, deviceCommunication, comCookie),
|
||||
primaryDataset(this),
|
||||
primaryDataset(sharedPool),
|
||||
transitionDelay(transitionDelay) {}
|
||||
|
||||
MgmRM3100Handler::~MgmRM3100Handler() {}
|
||||
@@ -307,12 +307,15 @@ void MgmRM3100Handler::fillCommandAndReplyMap() {
|
||||
|
||||
void MgmRM3100Handler::modeChanged() { internalState = InternalState::NONE; }
|
||||
|
||||
// TODO: Fix
|
||||
/*
|
||||
ReturnValue_t MgmRM3100Handler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) {
|
||||
PeriodicHkGenerationHelper &poolManager) {
|
||||
localDataPoolMap.emplace(mgmRm3100::FIELD_STRENGTHS, &mgmXYZ);
|
||||
poolManager.subscribeForRegularPeriodicPacket({primaryDataset.getSid(), false, 10.0});
|
||||
poolManager.setPeriodicFrequency(primaryDataset.getSid(), 10'000);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
*/
|
||||
|
||||
uint32_t MgmRM3100Handler::getTransitionDelayMs(Mode_t from, Mode_t to) {
|
||||
return this->transitionDelay;
|
||||
@@ -356,7 +359,7 @@ ReturnValue_t MgmRM3100Handler::handleDataReadout(const uint8_t *packet) {
|
||||
primaryDataset.fieldStrengths[0] = fieldStrengthX;
|
||||
primaryDataset.fieldStrengths[1] = fieldStrengthY;
|
||||
primaryDataset.fieldStrengths[2] = fieldStrengthZ;
|
||||
primaryDataset.setValidity(true, true);
|
||||
primaryDataset.valid = true;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef MISSION_DEVICES_MGMRM3100HANDLER_H_
|
||||
#define MISSION_DEVICES_MGMRM3100HANDLER_H_
|
||||
|
||||
#include <fsfw_hal/devicehandlers/devicedefinitions/mgmRm3100Helpers.h>
|
||||
#include "mgmRm3100Helpers.h"
|
||||
|
||||
#include "fsfw/devicehandlers/DeviceHandlerBase.h"
|
||||
#include "fsfw/globalfunctions/PeriodicOperationDivider.h"
|
||||
@@ -18,13 +18,13 @@ class MgmRM3100Handler : public DeviceHandlerBase {
|
||||
static const uint8_t INTERFACE_ID = CLASS_ID::MGM_RM3100;
|
||||
|
||||
//! [EXPORT] : [COMMENT] P1: TMRC value which was set, P2: 0
|
||||
static constexpr Event tmrcSet = event::makeEvent(SUBSYSTEM_ID::MGM_RM3100, 0x00, severity::INFO);
|
||||
static constexpr Event tmrcSet = event::makeEvent<SUBSYSTEM_ID::MGM_RM3100, 0x00, severity::INFO>();
|
||||
|
||||
//! [EXPORT] : [COMMENT] Cycle counter set. P1: First two bytes new Cycle Count X
|
||||
//! P1: Second two bytes new Cycle Count Y
|
||||
//! P2: New cycle count Z
|
||||
static constexpr Event cycleCountersSet =
|
||||
event::makeEvent(SUBSYSTEM_ID::MGM_RM3100, 0x01, severity::INFO);
|
||||
event::makeEvent<SUBSYSTEM_ID::MGM_RM3100, 0x01, severity::INFO>();
|
||||
|
||||
MgmRM3100Handler(object_id_t objectId, object_id_t deviceCommunication, CookieIF *comCookie,
|
||||
uint32_t transitionDelay);
|
||||
@@ -52,8 +52,8 @@ class MgmRM3100Handler : public DeviceHandlerBase {
|
||||
void fillCommandAndReplyMap() override;
|
||||
void modeChanged(void) override;
|
||||
virtual uint32_t getTransitionDelayMs(Mode_t from, Mode_t to) override;
|
||||
ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) override;
|
||||
// ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
// PeriodicHkGenerationHelper &poolManager) override;
|
||||
|
||||
private:
|
||||
enum class InternalState {
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_
|
||||
#define FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_
|
||||
|
||||
#include <fsfw/timemanager/clockDefinitions.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "fsfw/housekeeping/AcceptsHkPacketsIF.h"
|
||||
#include "fsfw/ipc/MessageQueueIF.h"
|
||||
#include "fsfw/ipc/messageQueueDefinitions.h"
|
||||
#include "fsfw/returnvalues/returnvalue.h"
|
||||
#include "localPoolDefinitions.h"
|
||||
|
||||
namespace subdp {
|
||||
|
||||
struct ParamsBase {
|
||||
ParamsBase(sid_t sid, bool enableReporting, dur_millis_t collectionIntervalMs)
|
||||
: sid(sid), enableReporting(enableReporting), collectionIntervalMs(collectionIntervalMs) {}
|
||||
|
||||
[[nodiscard]] bool isDiagnostics() const { return diagnostics; }
|
||||
|
||||
sid_t sid;
|
||||
bool enableReporting;
|
||||
dur_millis_t collectionIntervalMs;
|
||||
MessageQueueId_t receiver = MessageQueueIF::NO_QUEUE;
|
||||
|
||||
protected:
|
||||
bool diagnostics;
|
||||
};
|
||||
|
||||
struct RegularHkPeriodicParams : public ParamsBase {
|
||||
RegularHkPeriodicParams(sid_t sid, bool enableReporting, dur_millis_t collectionIntervalMs)
|
||||
: ParamsBase(sid, enableReporting, collectionIntervalMs) {}
|
||||
};
|
||||
|
||||
struct RegularHkUpdateParams : public ParamsBase {
|
||||
RegularHkUpdateParams(sid_t sid, bool enableReporting) : ParamsBase(sid, enableReporting, 0) {}
|
||||
};
|
||||
|
||||
struct DiagnosticsHkUpdateParams : public ParamsBase {
|
||||
DiagnosticsHkUpdateParams(sid_t sid, bool enableReporting)
|
||||
: ParamsBase(sid, enableReporting, 0) {}
|
||||
};
|
||||
} // namespace subdp
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_ */
|
||||
@@ -1,21 +1,23 @@
|
||||
#include "fsfw/datapoollocal/SharedLocalDataSet.h"
|
||||
#include "fsfw/datapool/SharedLocalDataset.h"
|
||||
|
||||
SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, sid_t sid, const size_t maxSize)
|
||||
: SystemObject(objectId), LocalPoolDataSetBase(sid, nullptr, maxSize), poolVarVector(maxSize) {
|
||||
SharedLocalDataset::SharedLocalDataset(object_id_t objectId, structure_id_t sid, const size_t maxSize)
|
||||
: SystemObject(objectId), SharedDatasetBase(sid, nullptr, maxSize), poolVarVector(maxSize) {
|
||||
this->setContainer(poolVarVector.data());
|
||||
datasetLock = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, HasLocalDataPoolIF *owner,
|
||||
SharedLocalDataset::SharedLocalDataset(object_id_t objectId, localpool::SharedPool& sharedPool,
|
||||
uint32_t setId, const size_t maxSize)
|
||||
: SystemObject(objectId),
|
||||
LocalPoolDataSetBase(owner, setId, nullptr, maxSize),
|
||||
SharedDatasetBase(sharedPool, setId, nullptr, maxSize),
|
||||
poolVarVector(maxSize) {
|
||||
this->setContainer(poolVarVector.data());
|
||||
datasetLock = MutexFactory::instance()->createMutex();
|
||||
}
|
||||
|
||||
ReturnValue_t SharedLocalDataSet::lockDataset(MutexIF::TimeoutType timeoutType,
|
||||
SharedLocalDataset::~SharedLocalDataset() { MutexFactory::instance()->deleteMutex(datasetLock); }
|
||||
|
||||
ReturnValue_t SharedLocalDataset::lockDataset(MutexIF::TimeoutType timeoutType,
|
||||
dur_millis_t mutexTimeout) {
|
||||
if (datasetLock != nullptr) {
|
||||
return datasetLock->lockMutex(timeoutType, mutexTimeout);
|
||||
@@ -23,9 +25,7 @@ ReturnValue_t SharedLocalDataSet::lockDataset(MutexIF::TimeoutType timeoutType,
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
SharedLocalDataSet::~SharedLocalDataSet() { MutexFactory::instance()->deleteMutex(datasetLock); }
|
||||
|
||||
ReturnValue_t SharedLocalDataSet::unlockDataset() {
|
||||
ReturnValue_t SharedLocalDataset::unlockDataset() {
|
||||
if (datasetLock != nullptr) {
|
||||
return datasetLock->unlockMutex();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "fsfw/datapool/SharedDataSetIF.h"
|
||||
#include "fsfw/objectmanager/SystemObject.h"
|
||||
#include "SharedSetBase.h"
|
||||
#include "SharedPool.h"
|
||||
|
||||
namespace datapool {
|
||||
|
||||
/**
|
||||
* This local dataset variation can be used if the dataset is used concurrently across
|
||||
* multiple threads. It provides a lock in addition to all other functionalities provided
|
||||
* by the LocalPoolDataSetBase class.
|
||||
*
|
||||
* The user is completely responsible for locking and unlocking the dataset when using the
|
||||
* shared dataset.
|
||||
*/
|
||||
class SharedLocalDataset : public SystemObject, public SharedSetBase, public SharedDataSetIF {
|
||||
public:
|
||||
SharedLocalDataset(object_id_t objectId, SharedPool& sharedPool, uint32_t setId,
|
||||
size_t maxSize);
|
||||
SharedLocalDataset(object_id_t objectId, sid_t sid, size_t maxSize);
|
||||
|
||||
~SharedLocalDataset() override;
|
||||
|
||||
ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
dur_millis_t mutexTimeout = 20) override;
|
||||
ReturnValue_t unlockDataset() override;
|
||||
|
||||
private:
|
||||
MutexIF* datasetLock = nullptr;
|
||||
std::vector<PoolVariableIF*> poolVarVector;
|
||||
};
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#include <fsfw_hal/devicehandlers/devicedefinitions/gyroL3gHelpers.h>
|
||||
#include "gyroL3gHelpers.h"
|
||||
|
||||
float l3gd20h::ctrlReg4ToSensitivity(uint8_t reg) {
|
||||
bool fsH = reg & l3gd20h::SET_FS_1;
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_GYROL3GD20DEFINITIONS_H_
|
||||
#define MISSION_DEVICES_DEVICEDEFINITIONS_GYROL3GD20DEFINITIONS_H_
|
||||
|
||||
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
|
||||
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
|
||||
#include "fsfw/datapoollocal/StaticLocalDataSet.h"
|
||||
#include "fsfw/devicehandlers/DeviceHandlerIF.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -124,8 +124,8 @@ class GyroPrimaryDataset : public StaticLocalDataSet<5> {
|
||||
setAllVariablesReadOnly();
|
||||
}
|
||||
/** Constructor for the data creator */
|
||||
GyroPrimaryDataset(HasLocalDataPoolIF* hkOwner)
|
||||
: StaticLocalDataSet(hkOwner, l3gd20h::GYRO_DATASET_ID) {}
|
||||
GyroPrimaryDataset(localpool::SharedPool& sharedPool)
|
||||
: StaticLocalDataSet(sharedPool, l3gd20h::GYRO_DATASET_ID) {}
|
||||
|
||||
/* Angular velocities in degrees per second (DPS) */
|
||||
lp_var_t<float> angVelocX = lp_var_t<float>(sid.objectId, l3gd20h::ANG_VELOC_X, this);
|
||||
File renamed without changes.
+7
-5
@@ -1,9 +1,9 @@
|
||||
#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_MGMLIS3HANDLERDEFS_H_
|
||||
#define MISSION_DEVICES_DEVICEDEFINITIONS_MGMLIS3HANDLERDEFS_H_
|
||||
|
||||
#include <fsfw/datapoollocal/LocalPoolVariable.h>
|
||||
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
|
||||
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
|
||||
#include "fsfw/datapoollocal/LocalPoolVariable.h"
|
||||
#include "fsfw/datapoollocal/StaticLocalDataSet.h"
|
||||
#include "fsfw/devicehandlers/DeviceHandlerIF.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -169,11 +169,12 @@ static const uint8_t CTRL_REG5_DEFAULT = 0;
|
||||
|
||||
static const uint32_t MGM_DATA_SET_ID = READ_CONFIG_AND_DATA;
|
||||
|
||||
enum MgmPoolIds : lp_id_t { FIELD_STRENGTHS, TEMPERATURE_CELCIUS };
|
||||
enum MgmPoolIds : lp_id_t { FIELD_STRENGTHS = 0, TEMPERATURE_CELCIUS = 1, SET_IS_VALID = 2 };
|
||||
|
||||
class MgmPrimaryDataset : public StaticLocalDataSet<4> {
|
||||
public:
|
||||
MgmPrimaryDataset(HasLocalDataPoolIF* hkOwner) : StaticLocalDataSet(hkOwner, MGM_DATA_SET_ID) {}
|
||||
MgmPrimaryDataset(localpool::SharedPool& sharedPool)
|
||||
: StaticLocalDataSet(sharedPool, MGM_DATA_SET_ID) {}
|
||||
|
||||
MgmPrimaryDataset(object_id_t mgmId) : StaticLocalDataSet(sid_t(mgmId, MGM_DATA_SET_ID)) {}
|
||||
|
||||
@@ -182,6 +183,7 @@ class MgmPrimaryDataset : public StaticLocalDataSet<4> {
|
||||
*/
|
||||
lp_vec_t<float, 3> fieldStrengths = lp_vec_t<float, 3>(sid.objectId, FIELD_STRENGTHS, this);
|
||||
lp_var_t<float> temperature = lp_var_t<float>(sid.objectId, TEMPERATURE_CELCIUS, this);
|
||||
lp_var_t<uint8_t> setIsValid = lp_var_t<uint8_t>(sid.objectId, SET_IS_VALID, this);
|
||||
};
|
||||
|
||||
} // namespace mgmLis3
|
||||
+9
-7
@@ -1,13 +1,13 @@
|
||||
#ifndef MISSION_DEVICES_DEVICEDEFINITIONS_MGMHANDLERRM3100DEFINITIONS_H_
|
||||
#define MISSION_DEVICES_DEVICEDEFINITIONS_MGMHANDLERRM3100DEFINITIONS_H_
|
||||
|
||||
#include <fsfw/datapoollocal/LocalPoolVariable.h>
|
||||
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
|
||||
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
|
||||
#include <fsfw/serialize/SerialLinkedListAdapter.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "fsfw/datapoollocal/LocalPoolVariable.h"
|
||||
#include "fsfw/datapoollocal/StaticLocalDataSet.h"
|
||||
#include "fsfw/devicehandlers/DeviceHandlerIF.h"
|
||||
#include "fsfw/serialize/SerialLinkedListAdapter.h"
|
||||
|
||||
namespace mgmRm3100 {
|
||||
|
||||
/* Actually 10, we round up a little bit */
|
||||
@@ -101,11 +101,12 @@ class CycleCountCommand : public SerialLinkedListAdapter<SerializeIF> {
|
||||
|
||||
static constexpr uint32_t MGM_DATASET_ID = READ_DATA;
|
||||
|
||||
enum MgmPoolIds : lp_id_t { FIELD_STRENGTHS };
|
||||
enum MgmPoolIds : lp_id_t { FIELD_STRENGTHS = 0, VALID = 1 };
|
||||
|
||||
class Rm3100PrimaryDataset : public StaticLocalDataSet<3> {
|
||||
public:
|
||||
Rm3100PrimaryDataset(HasLocalDataPoolIF* hkOwner) : StaticLocalDataSet(hkOwner, MGM_DATASET_ID) {}
|
||||
Rm3100PrimaryDataset(localpool::SharedPool& sharedPool)
|
||||
: StaticLocalDataSet(sharedPool, MGM_DATASET_ID) {}
|
||||
|
||||
Rm3100PrimaryDataset(object_id_t mgmId) : StaticLocalDataSet(sid_t(mgmId, MGM_DATASET_ID)) {}
|
||||
|
||||
@@ -113,6 +114,7 @@ class Rm3100PrimaryDataset : public StaticLocalDataSet<3> {
|
||||
* Field strenghts in uT
|
||||
*/
|
||||
lp_vec_t<float, 3> fieldStrengths = lp_vec_t<float, 3>(sid.objectId, FIELD_STRENGTHS, this);
|
||||
lp_var_t<uint8_t> valid = lp_var_t<uint8_t>(sid.objectId, VALID, this);
|
||||
};
|
||||
|
||||
} // namespace mgmRm3100
|
||||
@@ -71,6 +71,7 @@ static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120;
|
||||
static constexpr uint8_t FSFW_CSB_FIFO_DEPTH = 6;
|
||||
|
||||
static constexpr size_t FSFW_PRINT_BUFFER_SIZE = 124;
|
||||
static constexpr size_t FSFW_PRINT_BUFFER_AMOUNT = 32;
|
||||
|
||||
static constexpr size_t FSFW_MAX_TM_PACKET_SIZE = 2048;
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@ else
|
||||
echo "No ${cmake_fmt} tool found, not formatting CMake files"
|
||||
fi
|
||||
|
||||
cpp_format="clang-format"
|
||||
cpp_format="clang-format-19"
|
||||
file_selectors="-iname *.h -o -iname *.cpp -o -iname *.c -o -iname *.tpp"
|
||||
if command -v ${cpp_format} &> /dev/null; then
|
||||
for dir in ${folder_list[@]}; do
|
||||
echo "Auto-formatting ${dir} recursively"
|
||||
find ${dir} ${file_selectors} | xargs clang-format --style=file -i
|
||||
find ${dir} ${file_selectors} | xargs ${cpp_format} --style=file -i
|
||||
done
|
||||
else
|
||||
echo "No ${cpp_format} tool found, not formatting C++/C files"
|
||||
|
||||
@@ -7,7 +7,6 @@ add_subdirectory(cfdp)
|
||||
add_subdirectory(container)
|
||||
add_subdirectory(controller)
|
||||
add_subdirectory(datapool)
|
||||
add_subdirectory(datapoollocal)
|
||||
add_subdirectory(devicehandlers)
|
||||
add_subdirectory(events)
|
||||
add_subdirectory(fdir)
|
||||
|
||||
@@ -2,14 +2,20 @@
|
||||
|
||||
#include <etl/crc32.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "fsfw/FSFW.h"
|
||||
#include "fsfw/cfdp/pdu/AckPduCreator.h"
|
||||
#include "fsfw/cfdp/pdu/AckPduReader.h"
|
||||
#include "fsfw/cfdp/pdu/EofPduReader.h"
|
||||
#include "fsfw/cfdp/pdu/FileDataReader.h"
|
||||
#include "fsfw/cfdp/pdu/FinishedPduCreator.h"
|
||||
#include "fsfw/cfdp/pdu/HeaderReader.h"
|
||||
#include "fsfw/cfdp/pdu/KeepAlivePduCreator.h"
|
||||
#include "fsfw/cfdp/pdu/NakPduCreator.h"
|
||||
#include "fsfw/objectmanager.h"
|
||||
#include "fsfw/returnvalues/returnvalue.h"
|
||||
#include "fsfw/tmtcservices/TmTcMessage.h"
|
||||
|
||||
using namespace returnvalue;
|
||||
@@ -23,7 +29,13 @@ cfdp::DestHandler::DestHandler(PduSenderIF& pduSender, size_t pduBufSize, DestHa
|
||||
msgToUserVec(params.maxTlvsInOnePdu),
|
||||
transactionParams(params.maxFilenameLen),
|
||||
destParams(std::move(params)),
|
||||
fsfwParams(fsfwParams) {
|
||||
fsfwParams(fsfwParams),
|
||||
// At least one request has to fit, otherwise sendNakSequence() indexes an empty buffer and
|
||||
// the deferred lost segment procedure could not make progress anyway.
|
||||
nakSegmentBuf(std::max<size_t>(destParams.maxSegmentRequestsPerNakPdu, 1)),
|
||||
positiveAckTimer(0, false),
|
||||
nakTimer(0, false),
|
||||
checkTimer(0, false) {
|
||||
transactionParams.pduConf.direction = cfdp::Direction::TOWARDS_SENDER;
|
||||
}
|
||||
|
||||
@@ -40,8 +52,31 @@ const cfdp::DestHandler::FsmResult& cfdp::DestHandler::stateMachine(
|
||||
return fsmRes;
|
||||
}
|
||||
PduPacketIF& pduPacket = *optPduPacket;
|
||||
if (pduPacket.getPduType() == FILE_DATA or
|
||||
(pduPacket.getPduType() == FILE_DIRECTIVE and *pduPacket.getFileDirective() != METADATA)) {
|
||||
if (pduPacket.getPduType() == FILE_DIRECTIVE and
|
||||
*pduPacket.getFileDirective() == EOF_DIRECTIVE) {
|
||||
// D7 of the class 2 plan: an EOF PDU retransmitted after we already finished the
|
||||
// transaction still has to be acknowledged. Without this the sender keeps retransmitting
|
||||
// until its positive ACK limit and then declares a fault at the end of a transfer that
|
||||
// actually succeeded.
|
||||
result = ackInactiveEofPdu(pduPacket);
|
||||
if (result != OK) {
|
||||
fsmRes.result = result;
|
||||
return fsmRes;
|
||||
}
|
||||
return updateFsmRes(errorIdx);
|
||||
}
|
||||
if (pduPacket.getPduType() == FILE_DATA) {
|
||||
// In acknowledged mode a lost metadata PDU is recoverable: start the transaction from the
|
||||
// PDU header and ask for the metadata with a NAK of scope 0..0. Only acknowledged mode
|
||||
// transactions can do this, everything else stays an error.
|
||||
result = startMetadatalessTransaction(pduPacket);
|
||||
if (result == OK) {
|
||||
return updateFsmRes(errorIdx);
|
||||
}
|
||||
fsmRes.result = DEST_NON_METADATA_PDU_AS_FIRST_PDU;
|
||||
return fsmRes;
|
||||
}
|
||||
if (pduPacket.getPduType() == FILE_DIRECTIVE and *pduPacket.getFileDirective() != METADATA) {
|
||||
fsmRes.result = DEST_NON_METADATA_PDU_AS_FIRST_PDU;
|
||||
return fsmRes;
|
||||
}
|
||||
@@ -50,6 +85,11 @@ const cfdp::DestHandler::FsmResult& cfdp::DestHandler::stateMachine(
|
||||
return updateFsmRes(errorIdx);
|
||||
}
|
||||
|
||||
if (fsmRes.state == CfdpState::BUSY_CLASS_2_ACKED) {
|
||||
fsmAcked(optPduPacket, errorIdx);
|
||||
return updateFsmRes(errorIdx);
|
||||
}
|
||||
|
||||
if (fsmRes.state == CfdpState::BUSY_CLASS_1_NACKED) {
|
||||
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
|
||||
if (!optPduPacket.has_value()) {
|
||||
@@ -73,20 +113,88 @@ const cfdp::DestHandler::FsmResult& cfdp::DestHandler::stateMachine(
|
||||
checkAndHandleError(result, errorIdx);
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::SENDING_FINISHED_PDU) {
|
||||
result = sendFinishedPdu();
|
||||
checkAndHandleError(result, errorIdx);
|
||||
finish();
|
||||
// Class 1 has no ACK for the Finished PDU, so the transaction is done the moment it goes
|
||||
// out. It must actually go out first though: finishing on a failed send means the peer never
|
||||
// hears that a transfer which did succeed completed, and it has no way to ask again.
|
||||
if (trySendingFinishedPdu(errorIdx)) {
|
||||
finish();
|
||||
}
|
||||
return updateFsmRes(errorIdx);
|
||||
}
|
||||
if (fsmRes.state == CfdpState::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);
|
||||
}
|
||||
|
||||
void cfdp::DestHandler::fsmAcked(
|
||||
const std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket, uint8_t& errorIdx) {
|
||||
ReturnValue_t result;
|
||||
if (optPduPacket.has_value()) {
|
||||
PduPacketIF& pduPacket = *optPduPacket;
|
||||
if (pduPacket.getPduType() == FILE_DATA) {
|
||||
// Retransmitted segments are only expected while the file is still being received.
|
||||
// handleFileDataPdu writes at the PDU offset, so out of order writes are fine.
|
||||
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS or
|
||||
fsmRes.step == TransactionStep::WAITING_FOR_MISSING_DATA) {
|
||||
result = handleFileDataPdu(pduPacket);
|
||||
checkAndHandleError(result, errorIdx);
|
||||
}
|
||||
} else if (pduPacket.getPduType() == FILE_DIRECTIVE) {
|
||||
switch (*pduPacket.getFileDirective()) {
|
||||
case (METADATA): {
|
||||
// Either a duplicate, which startTransaction discards, or the retransmission we asked
|
||||
// for after a lost metadata PDU.
|
||||
result = handleMetadataPdu(pduPacket);
|
||||
checkAndHandleError(result, errorIdx);
|
||||
break;
|
||||
}
|
||||
case (EOF_DIRECTIVE): {
|
||||
result = handleEofPdu(pduPacket);
|
||||
checkAndHandleError(result, errorIdx);
|
||||
break;
|
||||
}
|
||||
case (ACK): {
|
||||
result = handleAckPdu(pduPacket);
|
||||
checkAndHandleError(result, errorIdx);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// D2: the ACK for the EOF PDU is emitted here, before TRANSFER_COMPLETION runs the checksum
|
||||
// pass over the whole received file. On an iOBC that pass takes seconds for a large file,
|
||||
// which is long enough for the sender's positive ACK timer to fire.
|
||||
if (fsmRes.step == TransactionStep::SENDING_ACK_PDU) {
|
||||
result = handleSendingAckPdu();
|
||||
checkAndHandleError(result, errorIdx);
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::WAITING_FOR_MISSING_DATA) {
|
||||
result = handleWaitingForMissingData();
|
||||
checkAndHandleError(result, errorIdx);
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::TRANSFER_COMPLETION) {
|
||||
result = handleTransferCompletion();
|
||||
checkAndHandleError(result, errorIdx);
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::SENDING_FINISHED_PDU) {
|
||||
if (not trySendingFinishedPdu(errorIdx)) {
|
||||
// Nothing went out, so there is no ACK to wait for: either the send is retried on the next
|
||||
// call, or the attempt budget ran out and the transaction has already been released.
|
||||
return;
|
||||
}
|
||||
// In acknowledged mode the Finished PDU is itself acknowledged, so the transaction is
|
||||
// retained until the ACK arrives instead of being finished right here.
|
||||
transactionParams.positiveAckCounter = 0;
|
||||
positiveAckTimer.setTimeout(transactionParams.remoteCfg->positiveAckTimerIntervalMs);
|
||||
fsmRes.step = TransactionStep::WAITING_FOR_FINISHED_ACK;
|
||||
return;
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::WAITING_FOR_FINISHED_ACK) {
|
||||
result = handleWaitingForFinishedAck();
|
||||
checkAndHandleError(result, errorIdx);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleMetadataPdu(const PduPacketIF& pduPacket) {
|
||||
// Process metadata PDU
|
||||
cfdp::StringLv sourceFileName;
|
||||
@@ -116,6 +224,11 @@ ReturnValue_t cfdp::DestHandler::handleFileDataPdu(const PduPacketIF& info) {
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
if (fsmRes.state == CfdpState::BUSY_CLASS_2_ACKED and not transactionParams.metadataReceived) {
|
||||
// The metadata PDU was lost, so there is no destination file name to write to yet. Discard
|
||||
// the payload; it is re-requested by the NAK sequence once the metadata has arrived.
|
||||
return OK;
|
||||
}
|
||||
size_t fileSegmentLen = 0;
|
||||
const uint8_t* fileData = fdInfo.getFileData(&fileSegmentLen);
|
||||
if (destParams.cfg.indicCfg.fileSegmentRecvIndicRequired) {
|
||||
@@ -148,8 +261,13 @@ ReturnValue_t cfdp::DestHandler::handleFileDataPdu(const PduPacketIF& info) {
|
||||
}
|
||||
transactionParams.deliveryStatus = FileDeliveryStatus::RETAINED_IN_FILESTORE;
|
||||
transactionParams.vfsErrorCount = 0;
|
||||
if (fdInfo.getOffset().value() + fileSegmentLen > transactionParams.progress) {
|
||||
transactionParams.progress = fdInfo.getOffset().value() + fileSegmentLen;
|
||||
const uint64_t offset = fdInfo.getOffset().value();
|
||||
const uint64_t endOfSegment = offset + fileSegmentLen;
|
||||
if (fsmRes.state == CfdpState::BUSY_CLASS_2_ACKED) {
|
||||
trackReceivedSegment(offset, endOfSegment);
|
||||
}
|
||||
if (endOfSegment > transactionParams.progress) {
|
||||
transactionParams.progress = endOfSegment;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -158,7 +276,7 @@ ReturnValue_t cfdp::DestHandler::handleEofPdu(const cfdp::PduPacketIF& info) {
|
||||
size_t pduSize = 0;
|
||||
const auto rawPdu = info.getRawPduData(pduSize);
|
||||
// Process EOF PDU
|
||||
EofInfo eofInfo(nullptr);
|
||||
EofInfo eofInfo(&eofFaultLocation);
|
||||
EofPduReader reader(rawPdu, pduSize, eofInfo);
|
||||
ReturnValue_t result = reader.parseData();
|
||||
if (result != OK) {
|
||||
@@ -177,12 +295,39 @@ ReturnValue_t cfdp::DestHandler::handleEofPdu(const cfdp::PduPacketIF& info) {
|
||||
if (destParams.cfg.indicCfg.eofRecvIndicRequired) {
|
||||
destParams.user.eofRecvIndication(getTransactionId());
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
|
||||
if (fsmRes.state == CfdpState::BUSY_CLASS_1_NACKED) {
|
||||
if (fsmRes.state == CfdpState::BUSY_CLASS_1_NACKED) {
|
||||
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS) {
|
||||
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
|
||||
} else if (fsmRes.state == CfdpState::BUSY_CLASS_2_ACKED) {
|
||||
fsmRes.step = TransactionStep::SENDING_ACK_PDU;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
if (fsmRes.state != CfdpState::BUSY_CLASS_2_ACKED) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
transactionParams.eofReceived = true;
|
||||
transactionParams.eofConditionCode = eofInfo.getConditionCode();
|
||||
if (eofInfo.getConditionCode() != ConditionCode::NO_ERROR) {
|
||||
// A Cancel EOF ends the transaction. Adopt its condition code so that transfer completion
|
||||
// reports the cancellation, instead of running a checksum pass over a file the sender has
|
||||
// already abandoned and then reporting a checksum failure for it.
|
||||
transactionParams.conditionCode = eofInfo.getConditionCode();
|
||||
}
|
||||
if (eofInfo.getConditionCode() == ConditionCode::NO_ERROR and
|
||||
transactionParams.fileSize.value() > transactionParams.progress) {
|
||||
// Everything between the highest offset seen so far and the file size announced by the EOF
|
||||
// PDU is missing. This is also the only gap that exists for a transfer which lost its tail.
|
||||
insertLostSegment(transactionParams.progress, transactionParams.fileSize.value());
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::RECEIVING_FILE_DATA_PDUS or
|
||||
fsmRes.step == TransactionStep::WAITING_FOR_MISSING_DATA) {
|
||||
// A duplicate EOF arriving during the lost segment procedure means our ACK did not make it
|
||||
// back, so re-enter the ACK step, which also re-issues the NAK sequence.
|
||||
fsmRes.step = TransactionStep::SENDING_ACK_PDU;
|
||||
} else {
|
||||
// Duplicate EOF for a transaction we are already completing. Re-acknowledge it without
|
||||
// disturbing the step we are in.
|
||||
return sendAckPdu(transactionParams.pduConf, FileDirective::EOF_DIRECTIVE,
|
||||
transactionParams.eofConditionCode, AckTransactionStatus::ACTIVE);
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
@@ -226,8 +371,12 @@ ReturnValue_t cfdp::DestHandler::handleMetadataParseError(ReturnValue_t result,
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::startTransaction(const MetadataPduReader& reader) {
|
||||
if (fsmRes.state != CfdpState::IDLE) {
|
||||
// According to standard, discard metadata PDU if we are busy
|
||||
// A metadata PDU received while busy is normally a duplicate and is discarded per the standard.
|
||||
// The one exception is an acknowledged transaction which was started from a file data or EOF
|
||||
// PDU because the metadata was lost: this is the retransmission our NAK of scope 0..0 asked for.
|
||||
const bool lateMetadata =
|
||||
fsmRes.state == CfdpState::BUSY_CLASS_2_ACKED and not transactionParams.metadataReceived;
|
||||
if (fsmRes.state != CfdpState::IDLE and not lateMetadata) {
|
||||
return OK;
|
||||
}
|
||||
ReturnValue_t result = OK;
|
||||
@@ -296,18 +445,21 @@ ReturnValue_t cfdp::DestHandler::startTransaction(const MetadataPduReader& reade
|
||||
#endif
|
||||
return FAILED;
|
||||
}
|
||||
if (reader.getTransmissionMode() == TransmissionMode::UNACKNOWLEDGED) {
|
||||
fsmRes.state = CfdpState::BUSY_CLASS_1_NACKED;
|
||||
} else if (reader.getTransmissionMode() == TransmissionMode::ACKNOWLEDGED) {
|
||||
fsmRes.state = CfdpState::BUSY_CLASS_2_ACKED;
|
||||
}
|
||||
if (transactionParams.metadataOnly) {
|
||||
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
|
||||
} else {
|
||||
// Kind of ugly, make FSM working on packet per packet basis..
|
||||
fsmRes.step = TransactionStep::TRANSACTION_START;
|
||||
fsmRes.step = TransactionStep::RECEIVING_FILE_DATA_PDUS;
|
||||
if (not lateMetadata) {
|
||||
if (reader.getTransmissionMode() == TransmissionMode::UNACKNOWLEDGED) {
|
||||
fsmRes.state = CfdpState::BUSY_CLASS_1_NACKED;
|
||||
} else if (reader.getTransmissionMode() == TransmissionMode::ACKNOWLEDGED) {
|
||||
fsmRes.state = CfdpState::BUSY_CLASS_2_ACKED;
|
||||
}
|
||||
if (transactionParams.metadataOnly) {
|
||||
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
|
||||
} else {
|
||||
// Kind of ugly, make FSM working on packet per packet basis..
|
||||
fsmRes.step = TransactionStep::TRANSACTION_START;
|
||||
fsmRes.step = TransactionStep::RECEIVING_FILE_DATA_PDUS;
|
||||
}
|
||||
}
|
||||
transactionParams.metadataReceived = true;
|
||||
auto& info = reader.getGenericInfo();
|
||||
transactionParams.checksumType = info.getChecksumType();
|
||||
transactionParams.closureRequested = info.isClosureRequested();
|
||||
@@ -338,13 +490,27 @@ cfdp::CfdpState cfdp::DestHandler::getCfdpState() const { return fsmRes.state; }
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleTransferCompletion() {
|
||||
ReturnValue_t result;
|
||||
if (transactionParams.checksumType != ChecksumType::NULL_CHECKSUM) {
|
||||
if (transactionParams.conditionCode != ConditionCode::NO_ERROR) {
|
||||
// The transaction was cancelled, for example because the NAK or check limit was reached.
|
||||
// Report that condition code in the Finished PDU rather than running a checksum pass over a
|
||||
// file which is known to be incomplete.
|
||||
transactionParams.deliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
|
||||
} else if (transactionParams.checksumType != ChecksumType::NULL_CHECKSUM) {
|
||||
result = checksumVerification();
|
||||
if (result != OK) {
|
||||
// TODO: Warning / error handling?
|
||||
}
|
||||
} else {
|
||||
transactionParams.conditionCode = ConditionCode::NO_ERROR;
|
||||
if (transactionParams.metadataOnly) {
|
||||
// Nothing was expected, so nothing is missing: a metadata only transaction - a proxy put
|
||||
// request, say - carries no file data and completes the moment its metadata arrives. Both
|
||||
// fields are otherwise left at the reset defaults, which report a successful transaction as
|
||||
// "Data Incomplete" and "Discard deliberately". That contradicts the NO_ERROR condition code
|
||||
// beside it, and it goes out in the Finished PDU, not just into the log.
|
||||
transactionParams.deliveryCode = FileDeliveryCode::DATA_COMPLETE;
|
||||
transactionParams.deliveryStatus = FileDeliveryStatus::FILE_STATUS_UNREPORTED;
|
||||
}
|
||||
}
|
||||
result = noticeOfCompletion();
|
||||
if (result != OK) {
|
||||
@@ -394,6 +560,7 @@ void cfdp::DestHandler::fileErrorHandler(Event event, ReturnValue_t result,
|
||||
}
|
||||
|
||||
void cfdp::DestHandler::finish() {
|
||||
destParams.lostSegmentsContainer.clear();
|
||||
transactionParams.reset();
|
||||
fsmRes.state = CfdpState::IDLE;
|
||||
fsmRes.step = TransactionStep::IDLE;
|
||||
@@ -450,6 +617,19 @@ ReturnValue_t cfdp::DestHandler::noticeOfCompletion() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::sendKeepAlivePdu() {
|
||||
Fss progress(transactionParams.progress);
|
||||
KeepAlivePduCreator keepAlivePdu(transactionParams.pduConf, progress);
|
||||
size_t serLen = 0;
|
||||
ReturnValue_t result =
|
||||
keepAlivePdu.serialize(pduBuf.data(), serLen, keepAlivePdu.getSerializedSize());
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
return pduSender.sendPdu(PduType::FILE_DIRECTIVE, FileDirective::KEEP_ALIVE, pduBuf.data(),
|
||||
serLen);
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::sendFinishedPdu() {
|
||||
FinishedInfo info(transactionParams.conditionCode, transactionParams.deliveryCode,
|
||||
transactionParams.deliveryStatus);
|
||||
@@ -467,7 +647,7 @@ ReturnValue_t cfdp::DestHandler::sendFinishedPdu() {
|
||||
fsfwParams.eventReporter->forwardEvent(events::SERIALIZATION_ERROR, result, 0);
|
||||
return result;
|
||||
}
|
||||
pduSender.sendPdu(PduType::FILE_DIRECTIVE, FileDirective::FINISH, pduBuf.data(), serLen);
|
||||
result = pduSender.sendPdu(PduType::FILE_DIRECTIVE, FileDirective::FINISH, pduBuf.data(), serLen);
|
||||
if (result != OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "cfdp::DestHandler::sendFinishedPdu: Sending PDU failed" << std::endl;
|
||||
@@ -479,6 +659,33 @@ ReturnValue_t cfdp::DestHandler::sendFinishedPdu() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool cfdp::DestHandler::trySendingFinishedPdu(uint8_t& errorIdx) {
|
||||
transactionParams.finishedSendAttempts++;
|
||||
const ReturnValue_t result = sendFinishedPdu();
|
||||
checkAndHandleError(result, errorIdx);
|
||||
if (result == OK) {
|
||||
return true;
|
||||
}
|
||||
if (transactionParams.finishedSendAttempts < destParams.maxFinishedPduSendAttempts) {
|
||||
// Leave the step where it is so the next state machine call retries the same PDU. A send
|
||||
// failure here is usually a full TM store, which drains on its own.
|
||||
return false;
|
||||
}
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "cfdp::DestHandler: giving up on the Finished PDU after "
|
||||
<< transactionParams.finishedSendAttempts << " attempts" << std::endl;
|
||||
#else
|
||||
sif::printWarning("cfdp::DestHandler: giving up on the Finished PDU after %u attempts\n",
|
||||
static_cast<unsigned>(transactionParams.finishedSendAttempts));
|
||||
#endif
|
||||
// Only the peer's notification is lost: the file is complete on disk and the local user
|
||||
// already got its indication from noticeOfCompletion(). So this is not a delivery fault, and
|
||||
// releasing the handler matters more than the notification - while a transaction is held, every
|
||||
// incoming metadata PDU is discarded and no new uplink can start.
|
||||
finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
cfdp::DestHandler::TransactionStep cfdp::DestHandler::getTransactionStep() const {
|
||||
return fsmRes.step;
|
||||
}
|
||||
@@ -496,6 +703,8 @@ const cfdp::TransactionId& cfdp::DestHandler::getTransactionId() const {
|
||||
return transactionParams.transactionId;
|
||||
}
|
||||
|
||||
uint64_t cfdp::DestHandler::getProgress() const { return transactionParams.progress; }
|
||||
|
||||
void cfdp::DestHandler::checkAndHandleError(ReturnValue_t result, uint8_t& errorIdx) {
|
||||
if (result != OK and errorIdx < 3) {
|
||||
fsmRes.errorCodes[errorIdx] = result;
|
||||
@@ -509,4 +718,356 @@ void cfdp::DestHandler::setEventReporter(EventReportingProxyIF& reporter) {
|
||||
|
||||
const cfdp::DestHandlerParams& cfdp::DestHandler::getDestHandlerParams() const {
|
||||
return destParams;
|
||||
}
|
||||
}
|
||||
|
||||
bool cfdp::DestHandler::pduBelongsToTransaction(const PduPacketIF& pduPacket) const {
|
||||
size_t pduSize = 0;
|
||||
const auto rawPdu = pduPacket.getRawPduData(pduSize);
|
||||
PduHeaderReader reader(rawPdu, pduSize);
|
||||
if (reader.parseData() != OK) {
|
||||
return false;
|
||||
}
|
||||
EntityId sourceId;
|
||||
reader.getSourceId(sourceId);
|
||||
TransactionSeqNum seqNum;
|
||||
reader.getTransactionSeqNum(seqNum);
|
||||
// Compared by value rather than with operator==, which also compares the encoded width: the
|
||||
// peer is free to use a different width than we do for the same number.
|
||||
return sourceId.getValue() == transactionParams.transactionId.entityId.getValue() and
|
||||
seqNum.getValue() == transactionParams.transactionId.seqNum.getValue();
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleAckPdu(const cfdp::PduPacketIF& info) {
|
||||
size_t pduSize = 0;
|
||||
const auto rawPdu = info.getRawPduData(pduSize);
|
||||
AckInfo ackInfo;
|
||||
AckPduReader reader(rawPdu, pduSize, ackInfo);
|
||||
ReturnValue_t result = reader.parseData();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
// ACKs for EOF PDUs belong to the source handler and are routed there, so the only ACK which
|
||||
// can legitimately reach the destination handler is the one for our Finished PDU.
|
||||
if (ackInfo.getAckedDirective() != FileDirective::FINISH) {
|
||||
return OK;
|
||||
}
|
||||
if (not pduBelongsToTransaction(info)) {
|
||||
// The CFDP handler routes ACK PDUs on the acknowledged directive alone, so a late ACK from
|
||||
// an earlier transaction would otherwise finish whichever one is running now.
|
||||
return OK;
|
||||
}
|
||||
if (fsmRes.step == TransactionStep::WAITING_FOR_FINISHED_ACK) {
|
||||
finish();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleSendingAckPdu() {
|
||||
ReturnValue_t result =
|
||||
sendAckPdu(transactionParams.pduConf, FileDirective::EOF_DIRECTIVE,
|
||||
transactionParams.eofConditionCode, AckTransactionStatus::ACTIVE);
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
if (transactionParams.eofConditionCode != ConditionCode::NO_ERROR) {
|
||||
// The sender cancelled the transaction. Nothing left to request, report what we have.
|
||||
transactionParams.conditionCode = transactionParams.eofConditionCode;
|
||||
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
|
||||
return OK;
|
||||
}
|
||||
if (isFileComplete()) {
|
||||
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
|
||||
return OK;
|
||||
}
|
||||
// D3: deferred lost segment procedure. The NAK sequence for everything known to be missing is
|
||||
// issued once, here, and then re-issued on NAK timer expiry.
|
||||
fsmRes.step = TransactionStep::WAITING_FOR_MISSING_DATA;
|
||||
transactionParams.nakCounter = 0;
|
||||
transactionParams.checkCounter = 0;
|
||||
result = sendNakSequence();
|
||||
nakTimer.setTimeout(transactionParams.remoteCfg->nakTimerIntervalMs);
|
||||
checkTimer.setTimeout(transactionParams.remoteCfg->checkTimerIntervalMs);
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleWaitingForMissingData() {
|
||||
if (isFileComplete()) {
|
||||
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
|
||||
return OK;
|
||||
}
|
||||
if (checkTimer.hasTimedOut()) {
|
||||
transactionParams.checkCounter++;
|
||||
checkTimer.resetTimer();
|
||||
if (transactionParams.checkCounter > transactionParams.remoteCfg->checkLimit) {
|
||||
// A7: without this an incomplete transfer pins the handler forever and no later uplink
|
||||
// can start.
|
||||
declareFault(ConditionCode::CHECK_LIMIT_REACHED);
|
||||
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
if (nakTimer.hasTimedOut()) {
|
||||
transactionParams.nakCounter++;
|
||||
nakTimer.resetTimer();
|
||||
if (transactionParams.nakCounter > transactionParams.remoteCfg->nakTimerExpirationLimit) {
|
||||
declareFault(ConditionCode::NAK_LIMIT_REACHED);
|
||||
fsmRes.step = TransactionStep::TRANSFER_COMPLETION;
|
||||
return OK;
|
||||
}
|
||||
return sendNakSequence();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::handleWaitingForFinishedAck() {
|
||||
if (not positiveAckTimer.hasTimedOut()) {
|
||||
return OK;
|
||||
}
|
||||
transactionParams.positiveAckCounter++;
|
||||
positiveAckTimer.resetTimer();
|
||||
if (transactionParams.positiveAckCounter >
|
||||
transactionParams.remoteCfg->positiveAckTimerExpirationLimit) {
|
||||
// The sender is not acknowledging our Finished PDU. The file itself is already written, so
|
||||
// report the fault and release the handler instead of holding the transaction forever.
|
||||
declareFault(ConditionCode::POSITIVE_ACK_LIMIT_REACHED);
|
||||
finish();
|
||||
return OK;
|
||||
}
|
||||
return sendFinishedPdu();
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::startMetadatalessTransaction(const PduPacketIF& pduPacket) {
|
||||
size_t pduSize = 0;
|
||||
const auto rawPdu = pduPacket.getRawPduData(pduSize);
|
||||
PduHeaderReader headerReader(rawPdu, pduSize);
|
||||
ReturnValue_t result = headerReader.parseData();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
if (headerReader.getTransmissionMode() != TransmissionMode::ACKNOWLEDGED) {
|
||||
// Unacknowledged mode has no way of recovering the metadata PDU, so this stays an error.
|
||||
return FAILED;
|
||||
}
|
||||
EntityId sourceId;
|
||||
headerReader.getSourceId(sourceId);
|
||||
if (not destParams.remoteCfgTable.getRemoteCfg(sourceId, &transactionParams.remoteCfg)) {
|
||||
return FAILED;
|
||||
}
|
||||
headerReader.fillConfig(transactionParams.pduConf);
|
||||
transactionParams.pduConf.crcFlag = transactionParams.remoteCfg->crcOnTransmission;
|
||||
transactionParams.pduConf.direction = Direction::TOWARDS_SENDER;
|
||||
transactionParams.transactionId.entityId = transactionParams.pduConf.sourceId;
|
||||
transactionParams.transactionId.seqNum = transactionParams.pduConf.seqNum;
|
||||
transactionParams.metadataReceived = false;
|
||||
transactionParams.metadataOnly = false;
|
||||
fsmRes.state = CfdpState::BUSY_CLASS_2_ACKED;
|
||||
fsmRes.step = TransactionStep::RECEIVING_FILE_DATA_PDUS;
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "cfdp::DestHandler: file data PDU without metadata, requesting metadata"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning("cfdp::DestHandler: file data PDU without metadata, requesting metadata\n");
|
||||
#endif
|
||||
// Nothing at all can be done before the metadata arrives, so this one NAK is not deferred.
|
||||
result = sendNakSequence();
|
||||
transactionParams.nakCounter = 0;
|
||||
transactionParams.checkCounter = 0;
|
||||
nakTimer.setTimeout(transactionParams.remoteCfg->nakTimerIntervalMs);
|
||||
checkTimer.setTimeout(transactionParams.remoteCfg->checkTimerIntervalMs);
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::ackInactiveEofPdu(const PduPacketIF& pduPacket) {
|
||||
size_t pduSize = 0;
|
||||
const auto rawPdu = pduPacket.getRawPduData(pduSize);
|
||||
PduHeaderReader headerReader(rawPdu, pduSize);
|
||||
ReturnValue_t result = headerReader.parseData();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
if (headerReader.getTransmissionMode() != TransmissionMode::ACKNOWLEDGED) {
|
||||
return DEST_NON_METADATA_PDU_AS_FIRST_PDU;
|
||||
}
|
||||
EofInfo eofInfo(&eofFaultLocation);
|
||||
EofPduReader eofReader(rawPdu, pduSize, eofInfo);
|
||||
result = eofReader.parseData();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
PduConfig conf;
|
||||
headerReader.fillConfig(conf);
|
||||
conf.direction = Direction::TOWARDS_SENDER;
|
||||
RemoteEntityCfg* remoteCfg = nullptr;
|
||||
EntityId sourceId;
|
||||
headerReader.getSourceId(sourceId);
|
||||
if (destParams.remoteCfgTable.getRemoteCfg(sourceId, &remoteCfg) and remoteCfg != nullptr) {
|
||||
conf.crcFlag = remoteCfg->crcOnTransmission;
|
||||
} else {
|
||||
conf.crcFlag = false;
|
||||
}
|
||||
return sendAckPdu(conf, FileDirective::EOF_DIRECTIVE, eofInfo.getConditionCode(),
|
||||
AckTransactionStatus::UNRECOGNIZED);
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::sendAckPdu(PduConfig& conf, FileDirective ackedDirective,
|
||||
ConditionCode conditionCode,
|
||||
AckTransactionStatus status) {
|
||||
// CFDP 5.2.4: the directive subtype code is 0b0001 for an acknowledged Finished PDU and
|
||||
// 0b0000 for every other acknowledged directive.
|
||||
AckInfo ackInfo(ackedDirective, conditionCode, status,
|
||||
ackedDirective == FileDirective::FINISH ? 1 : 0);
|
||||
AckPduCreator ackPdu(ackInfo, conf);
|
||||
size_t serLen = 0;
|
||||
ReturnValue_t result = ackPdu.serialize(pduBuf.data(), serLen, ackPdu.getSerializedSize());
|
||||
if (result != OK) {
|
||||
fsfwParams.eventReporter->forwardEvent(events::SERIALIZATION_ERROR, result, 0);
|
||||
return result;
|
||||
}
|
||||
result = pduSender.sendPdu(PduType::FILE_DIRECTIVE, FileDirective::ACK, pduBuf.data(), serLen);
|
||||
if (result != OK) {
|
||||
fsfwParams.eventReporter->forwardEvent(events::PDU_SEND_ERROR, result, 0);
|
||||
return result;
|
||||
}
|
||||
fsmRes.packetsSent++;
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::DestHandler::sendNakSequence() {
|
||||
const bool largeFile = transactionParams.pduConf.largeFile;
|
||||
const uint64_t endOfScope = transactionParams.eofReceived ? transactionParams.fileSize.value()
|
||||
: transactionParams.progress;
|
||||
ReturnValue_t worstResult = OK;
|
||||
size_t idx = 0;
|
||||
// D5: the segment requests of one NAK PDU are bounded. What does not fit is carried into the
|
||||
// next PDU of the sequence instead of being dropped or declared a fault.
|
||||
auto flushNak = [&]() {
|
||||
if (idx == 0) {
|
||||
return;
|
||||
}
|
||||
NakInfo nakInfo(Fss(0, largeFile), Fss(endOfScope, largeFile));
|
||||
size_t segLen = idx;
|
||||
size_t maxSegLen = nakSegmentBuf.size();
|
||||
nakInfo.setSegmentRequests(nakSegmentBuf.data(), &segLen, &maxSegLen);
|
||||
NakPduCreator nakPdu(transactionParams.pduConf, nakInfo);
|
||||
size_t serLen = 0;
|
||||
ReturnValue_t result = nakPdu.serialize(pduBuf.data(), serLen, nakPdu.getSerializedSize());
|
||||
if (result != OK) {
|
||||
fsfwParams.eventReporter->forwardEvent(events::SERIALIZATION_ERROR, result, 0);
|
||||
worstResult = result;
|
||||
idx = 0;
|
||||
return;
|
||||
}
|
||||
result = pduSender.sendPdu(PduType::FILE_DIRECTIVE, FileDirective::NAK, pduBuf.data(), serLen);
|
||||
if (result != OK) {
|
||||
fsfwParams.eventReporter->forwardEvent(events::PDU_SEND_ERROR, result, 0);
|
||||
worstResult = result;
|
||||
} else {
|
||||
fsmRes.packetsSent++;
|
||||
}
|
||||
idx = 0;
|
||||
};
|
||||
|
||||
if (not transactionParams.metadataReceived) {
|
||||
// CFDP 5.2.6: a segment request of 0 to 0 requests the metadata PDU.
|
||||
nakSegmentBuf[idx++] = {Fss(0, largeFile), Fss(0, largeFile)};
|
||||
if (idx == nakSegmentBuf.size()) {
|
||||
flushNak();
|
||||
}
|
||||
}
|
||||
for (const auto& lostSegment : destParams.lostSegmentsContainer) {
|
||||
nakSegmentBuf[idx++] = {Fss(lostSegment.first, largeFile), Fss(lostSegment.second, largeFile)};
|
||||
if (idx == nakSegmentBuf.size()) {
|
||||
flushNak();
|
||||
}
|
||||
}
|
||||
flushNak();
|
||||
return worstResult;
|
||||
}
|
||||
|
||||
bool cfdp::DestHandler::isFileComplete() const {
|
||||
// Class 2 completion is "no gaps left and EOF received", not the class 1 "progress reached the
|
||||
// file size": a transfer can pass the announced file size with a retransmission while a gap in
|
||||
// the middle is still outstanding.
|
||||
return transactionParams.metadataReceived and transactionParams.eofReceived and
|
||||
destParams.lostSegmentsContainer.empty() and
|
||||
transactionParams.progress >= transactionParams.fileSize.value();
|
||||
}
|
||||
|
||||
void cfdp::DestHandler::trackReceivedSegment(uint64_t offset, uint64_t endOfSegment) {
|
||||
if (offset > transactionParams.progress) {
|
||||
// Everything between the high water mark and this segment was skipped over.
|
||||
insertLostSegment(transactionParams.progress, offset);
|
||||
}
|
||||
removeReceivedRange(offset, endOfSegment);
|
||||
}
|
||||
|
||||
void cfdp::DestHandler::insertLostSegment(uint64_t start, uint64_t end) {
|
||||
if (end <= start) {
|
||||
return;
|
||||
}
|
||||
auto& container = destParams.lostSegmentsContainer;
|
||||
// Merge with every entry this range touches so the list cannot accumulate duplicate or
|
||||
// overlapping gaps when an EOF PDU is retransmitted.
|
||||
for (auto it = container.begin(); it != container.end();) {
|
||||
if (it->second < start or it->first > end) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
start = std::min(start, it->first);
|
||||
end = std::max(end, it->second);
|
||||
it = container.erase(it);
|
||||
}
|
||||
if (container.full()) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "cfdp::DestHandler: lost segment list full, gap dropped" << std::endl;
|
||||
#else
|
||||
sif::printWarning("cfdp::DestHandler: lost segment list full, gap dropped\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
container.insert({start, end});
|
||||
}
|
||||
|
||||
void cfdp::DestHandler::removeReceivedRange(uint64_t start, uint64_t end) {
|
||||
if (end <= start) {
|
||||
return;
|
||||
}
|
||||
auto& container = destParams.lostSegmentsContainer;
|
||||
// A received range can span several gaps, but only the first and the last of them can be left
|
||||
// with a remainder, so two pending re-insertions are always enough.
|
||||
std::array<etl::pair<uint64_t, uint64_t>, 2> pending{};
|
||||
size_t pendingLen = 0;
|
||||
for (auto it = container.begin(); it != container.end();) {
|
||||
if (it->second <= start or it->first >= end) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
const uint64_t gapStart = it->first;
|
||||
const uint64_t gapEnd = it->second;
|
||||
it = container.erase(it);
|
||||
if (gapStart < start and pendingLen < pending.size()) {
|
||||
pending[pendingLen++] = {gapStart, start};
|
||||
}
|
||||
if (gapEnd > end and pendingLen < pending.size()) {
|
||||
pending[pendingLen++] = {end, gapEnd};
|
||||
}
|
||||
}
|
||||
for (size_t pendingIdx = 0; pendingIdx < pendingLen; pendingIdx++) {
|
||||
if (container.full()) {
|
||||
break;
|
||||
}
|
||||
container.insert(pending[pendingIdx]);
|
||||
}
|
||||
}
|
||||
|
||||
void cfdp::DestHandler::declareFault(ConditionCode code) {
|
||||
transactionParams.conditionCode = code;
|
||||
transactionParams.deliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
|
||||
destParams.cfg.fhBase.reportFault(transactionParams.transactionId, code);
|
||||
}
|
||||
|
||||
size_t cfdp::DestHandler::getNumLostSegments() const {
|
||||
return destParams.lostSegmentsContainer.size();
|
||||
}
|
||||
|
||||
uint32_t cfdp::DestHandler::getNakCounter() const { return transactionParams.nakCounter; }
|
||||
@@ -14,10 +14,14 @@
|
||||
#include "fsfw/cfdp/handler/PduPacketIF.h"
|
||||
#include "fsfw/cfdp/handler/PduSenderIF.h"
|
||||
#include "fsfw/cfdp/handler/mib.h"
|
||||
#include "fsfw/cfdp/pdu/HeaderReader.h"
|
||||
#include "fsfw/cfdp/pdu/MetadataPduReader.h"
|
||||
#include "fsfw/cfdp/pdu/NakInfo.h"
|
||||
#include "fsfw/cfdp/pdu/PduConfig.h"
|
||||
#include "fsfw/cfdp/tlv/EntityIdTlv.h"
|
||||
#include "fsfw/cfdp/tlv/MessageToUserTlv.h"
|
||||
#include "fsfw/storagemanager/StorageManagerIF.h"
|
||||
#include "fsfw/timemanager/Countdown.h"
|
||||
#include "fsfw/tmtcservices/AcceptsTelemetryIF.h"
|
||||
|
||||
namespace cfdp {
|
||||
@@ -47,6 +51,17 @@ struct DestHandlerParams {
|
||||
LostSegmentsListBase& lostSegmentsContainer;
|
||||
uint8_t maxTlvsInOnePdu = 20;
|
||||
size_t maxFilenameLen = 255;
|
||||
//! Upper bound on the number of segment requests packed into a single NAK PDU. The remaining
|
||||
//! lost segments are carried over into the next NAK of the sequence, so this bounds the PDU
|
||||
//! size without bounding what can be requested. Each segment request is 8 bytes for a small
|
||||
//! file and 16 bytes for a large one.
|
||||
size_t maxSegmentRequestsPerNakPdu = 20;
|
||||
//! Attempts at sending the Finished PDU before the transaction is released without it. The
|
||||
//! send can fail on a transient downstream condition (a full TM store), and treating that as
|
||||
//! sent loses the peer's only notification that the transfer completed. The retry has to be
|
||||
//! bounded though: a busy destination handler discards incoming metadata PDUs, so holding a
|
||||
//! transaction forever means no later uplink can start.
|
||||
uint32_t maxFinishedPduSendAttempts = 5;
|
||||
};
|
||||
|
||||
class DestHandler {
|
||||
@@ -57,7 +72,13 @@ class DestHandler {
|
||||
RECEIVING_FILE_DATA_PDUS = 2,
|
||||
SENDING_ACK_PDU = 3,
|
||||
TRANSFER_COMPLETION = 4,
|
||||
SENDING_FINISHED_PDU = 5
|
||||
SENDING_FINISHED_PDU = 5,
|
||||
//! Class 2 only: the deferred lost segment procedure is running. NAK PDUs have been issued
|
||||
//! for the known gaps and the handler is waiting for the retransmissions.
|
||||
WAITING_FOR_MISSING_DATA = 6,
|
||||
//! Class 2 only: the Finished PDU was sent and its ACK is outstanding. The transaction is
|
||||
//! retained until the ACK arrives or the positive ACK limit is reached.
|
||||
WAITING_FOR_FINISHED_ACK = 7
|
||||
};
|
||||
|
||||
struct FsmResult {
|
||||
@@ -101,8 +122,14 @@ class DestHandler {
|
||||
|
||||
[[nodiscard]] CfdpState getCfdpState() const;
|
||||
[[nodiscard]] TransactionStep getTransactionStep() const;
|
||||
[[nodiscard]] uint64_t getProgress() const;
|
||||
ReturnValue_t sendKeepAlivePdu();
|
||||
[[nodiscard]] const TransactionId& getTransactionId() const;
|
||||
[[nodiscard]] const DestHandlerParams& getDestHandlerParams() const;
|
||||
//! Number of gaps currently tracked by the deferred lost segment procedure. Always 0 in class 1.
|
||||
[[nodiscard]] size_t getNumLostSegments() const;
|
||||
//! Number of NAK sequences issued for the current transaction.
|
||||
[[nodiscard]] uint32_t getNakCounter() const;
|
||||
|
||||
private:
|
||||
struct TransactionParams {
|
||||
@@ -126,8 +153,29 @@ class DestHandler {
|
||||
closureRequested = false;
|
||||
vfsErrorCount = 0;
|
||||
checksumType = ChecksumType::NULL_CHECKSUM;
|
||||
metadataReceived = false;
|
||||
eofReceived = false;
|
||||
eofConditionCode = ConditionCode::NO_ERROR;
|
||||
positiveAckCounter = 0;
|
||||
nakCounter = 0;
|
||||
checkCounter = 0;
|
||||
finishedSendAttempts = 0;
|
||||
}
|
||||
|
||||
//! Attempts made at sending the Finished PDU, see
|
||||
//! DestHandlerParams::maxFinishedPduSendAttempts. Used by both transmission modes.
|
||||
uint32_t finishedSendAttempts = 0;
|
||||
|
||||
//! Class 2 only: false while the transaction was started from a file data or EOF PDU because
|
||||
//! the metadata PDU was lost. The metadata is then requested with a NAK of scope 0..0.
|
||||
bool metadataReceived = false;
|
||||
bool eofReceived = false;
|
||||
//! Condition code of the received EOF PDU, needed to build the matching ACK PDU.
|
||||
ConditionCode eofConditionCode = ConditionCode::NO_ERROR;
|
||||
uint32_t positiveAckCounter = 0;
|
||||
uint32_t nakCounter = 0;
|
||||
uint32_t checkCounter = 0;
|
||||
|
||||
bool metadataOnly = false;
|
||||
ChecksumType checksumType = ChecksumType::NULL_CHECKSUM;
|
||||
bool closureRequested = false;
|
||||
@@ -153,6 +201,20 @@ class DestHandler {
|
||||
DestHandlerParams destParams;
|
||||
cfdp::FsfwParams fsfwParams;
|
||||
FsmResult fsmRes;
|
||||
//! Scratch space for the segment requests of one NAK PDU. Sized from
|
||||
//! DestHandlerParams::maxSegmentRequestsPerNakPdu.
|
||||
std::vector<NakInfo::SegmentRequest> nakSegmentBuf;
|
||||
//! Guards the Finished PDU, see RemoteEntityCfg::positiveAckTimerIntervalMs.
|
||||
Countdown positiveAckTimer;
|
||||
//! Drives the deferred lost segment procedure, see RemoteEntityCfg::nakTimerIntervalMs.
|
||||
Countdown nakTimer;
|
||||
//! Guards an incomplete transaction after EOF reception, see RemoteEntityCfg::checkLimit.
|
||||
Countdown checkTimer;
|
||||
//! Receives the fault location of an incoming EOF PDU. EofPduReader refuses to parse any EOF
|
||||
//! whose condition code is not NO_ERROR unless it has somewhere to put that TLV, so without
|
||||
//! these every Cancel EOF the sender emits would be dropped as unparseable.
|
||||
cfdp::EntityId eofFaultLocationId;
|
||||
EntityIdTlv eofFaultLocation{eofFaultLocationId};
|
||||
|
||||
ReturnValue_t startTransaction(const MetadataPduReader& reader);
|
||||
ReturnValue_t handleMetadataPdu(const PduPacketIF& pduPacket);
|
||||
@@ -160,9 +222,37 @@ class DestHandler {
|
||||
ReturnValue_t handleEofPdu(const PduPacketIF& info);
|
||||
ReturnValue_t handleMetadataParseError(ReturnValue_t result, const uint8_t* rawData,
|
||||
size_t maxSize);
|
||||
ReturnValue_t handleAckPdu(const PduPacketIF& info);
|
||||
//! True if the PDU's source entity ID and sequence number match the running transaction. The
|
||||
//! CFDP handler routes PDUs by direction only, so this is the only transaction level filter.
|
||||
[[nodiscard]] bool pduBelongsToTransaction(const PduPacketIF& pduPacket) const;
|
||||
ReturnValue_t handleTransferCompletion();
|
||||
//! Class 2 substates, driven from stateMachine().
|
||||
void fsmAcked(std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket, uint8_t& errorIdx);
|
||||
ReturnValue_t handleSendingAckPdu();
|
||||
ReturnValue_t handleWaitingForMissingData();
|
||||
ReturnValue_t handleWaitingForFinishedAck();
|
||||
ReturnValue_t startMetadatalessTransaction(const PduPacketIF& pduPacket);
|
||||
ReturnValue_t ackInactiveEofPdu(const PduPacketIF& pduPacket);
|
||||
ReturnValue_t sendAckPdu(PduConfig& conf, FileDirective ackedDirective,
|
||||
ConditionCode conditionCode, AckTransactionStatus status);
|
||||
ReturnValue_t sendNakSequence();
|
||||
[[nodiscard]] bool isFileComplete() const;
|
||||
void trackReceivedSegment(uint64_t offset, uint64_t endOfSegment);
|
||||
void insertLostSegment(uint64_t start, uint64_t end);
|
||||
void removeReceivedRange(uint64_t start, uint64_t end);
|
||||
void declareFault(ConditionCode code);
|
||||
ReturnValue_t tryBuildingAbsoluteDestName(size_t destNameSize);
|
||||
ReturnValue_t sendFinishedPdu();
|
||||
/**
|
||||
* Sends the Finished PDU, retrying a bounded number of times on a failed send.
|
||||
*
|
||||
* @return True if the PDU went out and the caller should advance. False means the caller must
|
||||
* leave the step alone and do nothing else this iteration: either the send is being
|
||||
* retried on the next call, or the attempt budget ran out and the transaction was
|
||||
* already released with finish().
|
||||
*/
|
||||
bool trySendingFinishedPdu(uint8_t& errorIdx);
|
||||
ReturnValue_t noticeOfCompletion();
|
||||
ReturnValue_t checksumVerification();
|
||||
void fileErrorHandler(Event event, ReturnValue_t result, const char* info) const;
|
||||
|
||||
@@ -14,7 +14,10 @@ cfdp::PutRequest::PutRequest(cfdp::EntityId destId, const uint8_t *msgsToUser,
|
||||
|
||||
cfdp::PutRequest::PutRequest(cfdp::EntityId destId, cfdp::StringLv &sourceName,
|
||||
cfdp::StringLv &destName)
|
||||
: destId(std::move(destId)), sourceName(std::move(sourceName)), destName(std::move(destName)) {}
|
||||
: destId(std::move(destId)),
|
||||
metadataOnly(false),
|
||||
sourceName(std::move(sourceName)),
|
||||
destName(std::move(destName)) {}
|
||||
|
||||
[[nodiscard]] bool cfdp::PutRequest::isMetadataOnly() const { return metadataOnly; }
|
||||
|
||||
|
||||
@@ -4,9 +4,15 @@
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "fsfw/cfdp/pdu/AckPduCreator.h"
|
||||
#include "fsfw/cfdp/pdu/AckPduReader.h"
|
||||
#include "fsfw/cfdp/pdu/EofPduCreator.h"
|
||||
#include "fsfw/cfdp/pdu/FileDataCreator.h"
|
||||
#include "fsfw/cfdp/pdu/FileDirectiveReader.h"
|
||||
#include "fsfw/cfdp/pdu/FinishedPduReader.h"
|
||||
#include "fsfw/cfdp/pdu/HeaderReader.h"
|
||||
#include "fsfw/cfdp/pdu/MetadataPduCreator.h"
|
||||
#include "fsfw/cfdp/pdu/NakPduReader.h"
|
||||
#include "fsfw/filesystem/HasFileSystemIF.h"
|
||||
#include "fsfw/globalfunctions/arrayprinter.h"
|
||||
#include "fsfw/objectmanager.h"
|
||||
@@ -16,12 +22,28 @@
|
||||
|
||||
using namespace returnvalue;
|
||||
|
||||
namespace {
|
||||
|
||||
//! True if the PDU is long enough to hold the Finished PDU fields which are mandatory, i.e. the
|
||||
//! directive byte plus the byte carrying the condition code, delivery code and file status.
|
||||
//! Anything shorter cannot be trusted even partially.
|
||||
bool mandatoryFinishedFieldsPresent(const uint8_t* rawPdu, size_t pduSize) {
|
||||
FileDirectiveReader directiveReader(rawPdu, pduSize);
|
||||
if (directiveReader.parseData() != returnvalue::OK) {
|
||||
return false;
|
||||
}
|
||||
return directiveReader.getWholePduSize() > directiveReader.getHeaderSize();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
cfdp::SourceHandler::SourceHandler(PduSenderIF& pduSender, size_t pduBufferSize,
|
||||
SourceHandlerParams params, FsfwParams fsfwParams)
|
||||
: pduSender(pduSender),
|
||||
pduBuf(pduBufferSize),
|
||||
sourceParams(std::move(params)),
|
||||
fsfwParams(fsfwParams) {
|
||||
fsfwParams(fsfwParams),
|
||||
positiveAckTimer(0, false) {
|
||||
// The entity ID portion of the transaction ID will always remain fixed.
|
||||
transactionParams.id.entityId = sourceParams.cfg.localId;
|
||||
transactionParams.pduConf.sourceId = sourceParams.cfg.localId;
|
||||
@@ -47,8 +69,19 @@ cfdp::SourceHandler::SourceHandler(PduSenderIF& pduSender, size_t pduBufferSize,
|
||||
transactionParams.pduConf.seqNum.setValue(0);
|
||||
}
|
||||
|
||||
cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmNacked() {
|
||||
cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmNacked(
|
||||
const std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket) {
|
||||
ReturnValue_t result;
|
||||
if (optPduPacket.has_value() and step == TransactionStep::WAIT_FOR_FINISH) {
|
||||
PduPacketIF& pduPacket = *optPduPacket;
|
||||
if (pduPacket.getPduType() == FILE_DIRECTIVE and *pduPacket.getFileDirective() == FINISH and
|
||||
pduBelongsToTransaction(pduPacket)) {
|
||||
result = handleFinishedPdu(pduPacket);
|
||||
if (result != OK) {
|
||||
addError(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (step == TransactionStep::IDLE) {
|
||||
step = TransactionStep::TRANSACTION_START;
|
||||
}
|
||||
@@ -86,6 +119,11 @@ cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmNacked() {
|
||||
}
|
||||
if (transactionParams.closureRequested) {
|
||||
step = TransactionStep::WAIT_FOR_FINISH;
|
||||
// The Finished PDU is now actually waited for. Bound the wait with the positive ACK timer
|
||||
// configuration so a lost Finished PDU cannot pin the handler: without closure the
|
||||
// transaction completed immediately, so a hang here would be a regression.
|
||||
transactionParams.positiveAckCounter = 0;
|
||||
positiveAckTimer.setTimeout(transactionParams.remoteCfg.positiveAckTimerIntervalMs);
|
||||
// fsmResult.callStatus = CallStatus::CALL_AFTER_DELAY;
|
||||
} else {
|
||||
step = TransactionStep::NOTICE_OF_COMPLETION;
|
||||
@@ -94,7 +132,20 @@ cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmNacked() {
|
||||
return fsmResult;
|
||||
}
|
||||
if (step == TransactionStep::WAIT_FOR_FINISH) {
|
||||
// TODO: In case this is a request with closure, wait for finish.
|
||||
if (not transactionParams.finishedReceived) {
|
||||
if (not positiveAckTimer.hasTimedOut()) {
|
||||
return fsmResult;
|
||||
}
|
||||
transactionParams.positiveAckCounter++;
|
||||
positiveAckTimer.resetTimer();
|
||||
if (transactionParams.positiveAckCounter <=
|
||||
transactionParams.remoteCfg.positiveAckTimerExpirationLimit) {
|
||||
return fsmResult;
|
||||
}
|
||||
// Unacknowledged mode has no retransmission, so the only thing left is to report that the
|
||||
// peer never confirmed the transfer.
|
||||
declareFault(ConditionCode::INACTIVITY_DETECTED);
|
||||
}
|
||||
// Done, issue notice of completion
|
||||
step = TransactionStep::NOTICE_OF_COMPLETION;
|
||||
}
|
||||
@@ -117,7 +168,10 @@ const cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::stateMachine(
|
||||
return fsmResult;
|
||||
}
|
||||
if (state == cfdp::CfdpState::BUSY_CLASS_1_NACKED) {
|
||||
return fsmNacked();
|
||||
return fsmNacked(optPduPacket);
|
||||
}
|
||||
if (state == cfdp::CfdpState::BUSY_CLASS_2_ACKED) {
|
||||
return fsmAcked(optPduPacket);
|
||||
}
|
||||
return fsmResult;
|
||||
}
|
||||
@@ -210,7 +264,10 @@ ReturnValue_t cfdp::SourceHandler::transactionStart(PutRequest& putRequest, Remo
|
||||
state = cfdp::CfdpState::BUSY_CLASS_2_ACKED;
|
||||
} else if (transactionParams.pduConf.mode == TransmissionMode::UNACKNOWLEDGED) {
|
||||
state = cfdp::CfdpState::BUSY_CLASS_1_NACKED;
|
||||
} else {
|
||||
return TRANSMISSION_MODE_NOT_SUPPORTED;
|
||||
}
|
||||
retransmitState.reset();
|
||||
step = TransactionStep::IDLE;
|
||||
uint64_t fileSize = 0;
|
||||
sourceParams.user.vfs.getFileSize(transactionParams.sourceName.data(), fileSize);
|
||||
@@ -230,15 +287,7 @@ ReturnValue_t cfdp::SourceHandler::transactionStart(PutRequest& putRequest, Remo
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::SourceHandler::prepareAndSendMetadataPdu() {
|
||||
cfdp::StringLv sourceName(transactionParams.sourceName.data(), transactionParams.sourceNameSize);
|
||||
cfdp::StringLv destName(transactionParams.destName.data(), transactionParams.destNameSize);
|
||||
auto metadataInfo =
|
||||
MetadataGenericInfo(transactionParams.closureRequested, transactionParams.checksumType,
|
||||
transactionParams.fileSize);
|
||||
auto metadataPdu =
|
||||
MetadataPduCreator(transactionParams.pduConf, metadataInfo, sourceName, destName, nullptr, 0);
|
||||
ReturnValue_t result =
|
||||
sendGenericPdu(PduType::FILE_DIRECTIVE, FileDirective::METADATA, metadataPdu);
|
||||
ReturnValue_t result = sendMetadataPdu();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
@@ -247,8 +296,18 @@ ReturnValue_t cfdp::SourceHandler::prepareAndSendMetadataPdu() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::SourceHandler::sendMetadataPdu() {
|
||||
cfdp::StringLv sourceName(transactionParams.sourceName.data(), transactionParams.sourceNameSize);
|
||||
cfdp::StringLv destName(transactionParams.destName.data(), transactionParams.destNameSize);
|
||||
auto metadataInfo =
|
||||
MetadataGenericInfo(transactionParams.closureRequested, transactionParams.checksumType,
|
||||
transactionParams.fileSize);
|
||||
auto metadataPdu =
|
||||
MetadataPduCreator(transactionParams.pduConf, metadataInfo, sourceName, destName, nullptr, 0);
|
||||
return sendGenericPdu(PduType::FILE_DIRECTIVE, FileDirective::METADATA, metadataPdu);
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::SourceHandler::prepareAndSendNextFileDataPdu(bool& noFileDataPdu) {
|
||||
cfdp::Fss offset(transactionParams.progress);
|
||||
uint64_t lenToRead;
|
||||
uint64_t fileSize = transactionParams.fileSize.value();
|
||||
noFileDataPdu = false;
|
||||
@@ -267,19 +326,7 @@ ReturnValue_t cfdp::SourceHandler::prepareAndSendNextFileDataPdu(bool& noFileDat
|
||||
lenToRead = transactionParams.remoteCfg.maxFileSegmentLen;
|
||||
}
|
||||
}
|
||||
FileOpParams fileParams(transactionParams.sourceName.data(), lenToRead);
|
||||
fileParams.offset = transactionParams.progress;
|
||||
size_t readLen = 0;
|
||||
ReturnValue_t result = sourceParams.user.vfs.readFromFile(
|
||||
transactionParams.sourceName.data(), transactionParams.progress, lenToRead, fileBuf.data(),
|
||||
readLen, fileBuf.size());
|
||||
if (result != returnvalue::OK) {
|
||||
addError(result);
|
||||
return result;
|
||||
}
|
||||
auto fileDataInfo = FileDataInfo(offset, fileBuf.data(), lenToRead);
|
||||
auto fileDataPdu = FileDataCreator(transactionParams.pduConf, fileDataInfo);
|
||||
result = sendGenericPdu(PduType::FILE_DATA, std::nullopt, fileDataPdu);
|
||||
ReturnValue_t result = sendFileDataPdu(transactionParams.progress, lenToRead);
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
@@ -319,20 +366,23 @@ ReturnValue_t cfdp::SourceHandler::sendGenericPdu(PduType pduType,
|
||||
addError(result);
|
||||
return result;
|
||||
}
|
||||
pduSender.sendPdu(pduType, fileDirective, pduBuf.data(), serializedLen);
|
||||
result = pduSender.sendPdu(pduType, fileDirective, pduBuf.data(), serializedLen);
|
||||
if (result != OK) {
|
||||
addError(result);
|
||||
return result;
|
||||
}
|
||||
fsmResult.packetsSent += 1;
|
||||
return result;
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::SourceHandler::noticeOfCompletion() {
|
||||
if (sourceParams.cfg.indicCfg.transactionFinishedIndicRequired) {
|
||||
// TODO: This could still be improved by caching the Finished PDU parameters.
|
||||
FileDeliveryStatus deliveryStatus = FileDeliveryStatus::FILE_STATUS_UNREPORTED;
|
||||
if (transactionParams.closureRequested) {
|
||||
deliveryStatus = FileDeliveryStatus::RETAINED_IN_FILESTORE;
|
||||
}
|
||||
cfdp::TransactionFinishedParams params(transactionParams.id, ConditionCode::NO_ERROR,
|
||||
FileDeliveryCode::DATA_COMPLETE, deliveryStatus);
|
||||
// The values reported by the peer's Finished PDU, if one was received. Reporting a hardcoded
|
||||
// NO_ERROR / DATA_COMPLETE here meant a transfer the receiver rejected still looked
|
||||
// successful on this side.
|
||||
cfdp::TransactionFinishedParams params(
|
||||
transactionParams.id, transactionParams.finishedConditionCode,
|
||||
transactionParams.finishedDeliveryCode, transactionParams.finishedDeliveryStatus);
|
||||
sourceParams.user.transactionFinishedIndication(params);
|
||||
}
|
||||
return OK;
|
||||
@@ -341,6 +391,7 @@ ReturnValue_t cfdp::SourceHandler::noticeOfCompletion() {
|
||||
ReturnValue_t cfdp::SourceHandler::reset() {
|
||||
step = TransactionStep::IDLE;
|
||||
state = cfdp::CfdpState::IDLE;
|
||||
retransmitState.reset();
|
||||
// fsmResult.callStatus = CallStatus::DONE;
|
||||
transactionParams.reset();
|
||||
return OK;
|
||||
@@ -356,3 +407,328 @@ void cfdp::SourceHandler::addError(ReturnValue_t error) {
|
||||
fsmResult.result = error;
|
||||
}
|
||||
}
|
||||
|
||||
cfdp::SourceHandler::FsmResult& cfdp::SourceHandler::fsmAcked(
|
||||
const std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket) {
|
||||
ReturnValue_t result;
|
||||
if (optPduPacket.has_value()) {
|
||||
handleAckedPdu(*optPduPacket);
|
||||
}
|
||||
if (step == TransactionStep::IDLE) {
|
||||
step = TransactionStep::TRANSACTION_START;
|
||||
}
|
||||
if (step == TransactionStep::TRANSACTION_START) {
|
||||
sourceParams.user.transactionIndication(transactionParams.id);
|
||||
result = checksumGeneration();
|
||||
if (result != OK) {
|
||||
addError(result);
|
||||
}
|
||||
step = TransactionStep::SENDING_METADATA;
|
||||
}
|
||||
if (step == TransactionStep::SENDING_METADATA) {
|
||||
result = prepareAndSendMetadataPdu();
|
||||
if (result != OK) {
|
||||
addError(result);
|
||||
}
|
||||
return fsmResult;
|
||||
}
|
||||
if (step == TransactionStep::SENDING_FILE_DATA) {
|
||||
bool noFdPdu = false;
|
||||
result = prepareAndSendNextFileDataPdu(noFdPdu);
|
||||
if (result == OK and !noFdPdu) {
|
||||
return fsmResult;
|
||||
}
|
||||
}
|
||||
if (step == TransactionStep::SENDING_EOF) {
|
||||
result = prepareAndSendEofPdu();
|
||||
if (result != OK) {
|
||||
addError(result);
|
||||
}
|
||||
if (sourceParams.cfg.indicCfg.eofSentIndicRequired) {
|
||||
sourceParams.user.eofSentIndication(transactionParams.id);
|
||||
}
|
||||
// The EOF PDU is acknowledged in class 2. Closure is meaningless here (D8 of the class 2
|
||||
// plan): the Finished PDU is mandatory, so the transaction always runs to WAIT_FOR_FINISH.
|
||||
transactionParams.positiveAckCounter = 0;
|
||||
positiveAckTimer.setTimeout(transactionParams.remoteCfg.positiveAckTimerIntervalMs);
|
||||
step = TransactionStep::WAIT_FOR_ACK;
|
||||
return fsmResult;
|
||||
}
|
||||
// Retransmissions requested by the peer take priority over any timer work: a source which
|
||||
// cannot answer a NAK deadlocks the transfer. One PDU per call keeps the burst bounded in
|
||||
// exactly the same way the regular file data phase is.
|
||||
if (servicePendingRetransmissions(result)) {
|
||||
if (result != OK) {
|
||||
addError(result);
|
||||
}
|
||||
return fsmResult;
|
||||
}
|
||||
if (step == TransactionStep::WAIT_FOR_ACK) {
|
||||
if (not positiveAckTimer.hasTimedOut()) {
|
||||
return fsmResult;
|
||||
}
|
||||
transactionParams.positiveAckCounter++;
|
||||
positiveAckTimer.resetTimer();
|
||||
if (transactionParams.positiveAckCounter >
|
||||
transactionParams.remoteCfg.positiveAckTimerExpirationLimit) {
|
||||
declareFault(ConditionCode::POSITIVE_ACK_LIMIT_REACHED);
|
||||
noticeOfCompletion();
|
||||
reset();
|
||||
return fsmResult;
|
||||
}
|
||||
result = prepareAndSendEofPdu();
|
||||
if (result != OK) {
|
||||
addError(result);
|
||||
}
|
||||
return fsmResult;
|
||||
}
|
||||
if (step == TransactionStep::WAIT_FOR_FINISH) {
|
||||
if (not transactionParams.finishedReceived) {
|
||||
if (not positiveAckTimer.hasTimedOut()) {
|
||||
return fsmResult;
|
||||
}
|
||||
transactionParams.positiveAckCounter++;
|
||||
positiveAckTimer.resetTimer();
|
||||
if (transactionParams.positiveAckCounter <=
|
||||
transactionParams.remoteCfg.positiveAckTimerExpirationLimit) {
|
||||
return fsmResult;
|
||||
}
|
||||
// The receiver retransmits its Finished PDU on its own positive ACK timer, so reaching
|
||||
// this point means the downlink is gone rather than a single PDU being lost.
|
||||
declareFault(ConditionCode::INACTIVITY_DETECTED);
|
||||
}
|
||||
step = TransactionStep::NOTICE_OF_COMPLETION;
|
||||
}
|
||||
if (step == TransactionStep::NOTICE_OF_COMPLETION) {
|
||||
noticeOfCompletion();
|
||||
reset();
|
||||
}
|
||||
return fsmResult;
|
||||
}
|
||||
|
||||
bool cfdp::SourceHandler::pduBelongsToTransaction(const PduPacketIF& pduPacket) const {
|
||||
size_t pduSize = 0;
|
||||
const auto rawPdu = pduPacket.getRawPduData(pduSize);
|
||||
PduHeaderReader reader(rawPdu, pduSize);
|
||||
if (reader.parseData() != OK) {
|
||||
return false;
|
||||
}
|
||||
EntityId sourceId;
|
||||
reader.getSourceId(sourceId);
|
||||
TransactionSeqNum seqNum;
|
||||
reader.getTransactionSeqNum(seqNum);
|
||||
// Compared by value rather than with operator==, which also compares the encoded width: the
|
||||
// peer is free to use a different width than we do for the same number.
|
||||
return sourceId.getValue() == transactionParams.id.entityId.getValue() and
|
||||
seqNum.getValue() == transactionParams.id.seqNum.getValue();
|
||||
}
|
||||
|
||||
void cfdp::SourceHandler::handleAckedPdu(PduPacketIF& pduPacket) {
|
||||
if (pduPacket.getPduType() != FILE_DIRECTIVE) {
|
||||
return;
|
||||
}
|
||||
if (not pduBelongsToTransaction(pduPacket)) {
|
||||
// Nothing upstream filters by transaction: the CFDP handler routes on the PDU direction
|
||||
// alone. A late ACK or Finished PDU from an earlier transaction would otherwise drive
|
||||
// whichever transaction is running now.
|
||||
return;
|
||||
}
|
||||
ReturnValue_t result = OK;
|
||||
switch (*pduPacket.getFileDirective()) {
|
||||
case (FileDirective::ACK): {
|
||||
result = handleAckPdu(pduPacket);
|
||||
break;
|
||||
}
|
||||
case (FileDirective::NAK): {
|
||||
result = handleNakPdu(pduPacket);
|
||||
break;
|
||||
}
|
||||
case (FileDirective::FINISH): {
|
||||
result = handleFinishedPdu(pduPacket);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Keep Alive PDUs carry progress information only, there is nothing to drive from them.
|
||||
break;
|
||||
}
|
||||
if (result != OK) {
|
||||
addError(result);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::SourceHandler::handleAckPdu(const PduPacketIF& pduPacket) {
|
||||
size_t pduSize = 0;
|
||||
const auto rawPdu = pduPacket.getRawPduData(pduSize);
|
||||
AckInfo ackInfo;
|
||||
AckPduReader reader(rawPdu, pduSize, ackInfo);
|
||||
ReturnValue_t result = reader.parseData();
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
// ACKs for Finished PDUs are routed to the destination handler, so only the EOF ACK can
|
||||
// legitimately arrive here.
|
||||
if (ackInfo.getAckedDirective() != FileDirective::EOF_DIRECTIVE) {
|
||||
return OK;
|
||||
}
|
||||
if (step == TransactionStep::WAIT_FOR_ACK) {
|
||||
step = TransactionStep::WAIT_FOR_FINISH;
|
||||
// Re-arm the same timer as an inactivity guard for the Finished PDU.
|
||||
transactionParams.positiveAckCounter = 0;
|
||||
positiveAckTimer.setTimeout(transactionParams.remoteCfg.positiveAckTimerIntervalMs);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::SourceHandler::handleFinishedPdu(const PduPacketIF& pduPacket) {
|
||||
size_t pduSize = 0;
|
||||
const auto rawPdu = pduPacket.getRawPduData(pduSize);
|
||||
FinishedInfo finishedInfo;
|
||||
FinishPduReader reader(rawPdu, pduSize, finishedInfo);
|
||||
// The condition code, delivery code and file status are parsed before any TLV, so they are
|
||||
// usable even if this handler cannot hold the optional filestore responses.
|
||||
ReturnValue_t result = reader.parseData();
|
||||
if (result != OK and not mandatoryFinishedFieldsPresent(rawPdu, pduSize)) {
|
||||
// The PDU is truncated before those fields, so it says nothing at all about how the transfer
|
||||
// went. Completing the transaction on it would report the default constructed DATA_COMPLETE,
|
||||
// i.e. a fabricated success, and would discard the state the peer's retransmission needs.
|
||||
return result;
|
||||
}
|
||||
transactionParams.finishedReceived = true;
|
||||
transactionParams.finishedConditionCode = finishedInfo.getConditionCode();
|
||||
transactionParams.finishedDeliveryCode = finishedInfo.getDeliveryCode();
|
||||
transactionParams.finishedDeliveryStatus = finishedInfo.getFileStatus();
|
||||
if (state == CfdpState::BUSY_CLASS_2_ACKED) {
|
||||
// The Finished PDU is acknowledged in class 2. This has to happen even for a duplicate,
|
||||
// because a duplicate means our previous ACK was lost.
|
||||
ReturnValue_t ackResult =
|
||||
sendAckPdu(FileDirective::FINISH, transactionParams.finishedConditionCode);
|
||||
if (ackResult != OK) {
|
||||
return ackResult;
|
||||
}
|
||||
}
|
||||
if (result != OK) {
|
||||
// Only the optional TLVs failed to parse, the delivery result above is still valid.
|
||||
return OK;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::SourceHandler::handleNakPdu(const PduPacketIF& pduPacket) {
|
||||
size_t pduSize = 0;
|
||||
const auto rawPdu = pduPacket.getRawPduData(pduSize);
|
||||
NakInfo nakInfo(Fss(0), Fss(0));
|
||||
size_t maxSegments = retransmitState.segments.size();
|
||||
size_t segmentLen = 0;
|
||||
nakInfo.setSegmentRequests(retransmitState.segments.data(), &segmentLen, &maxSegments);
|
||||
// The reader writes straight into the segment array, so the previous NAK's bookkeeping is
|
||||
// invalid the moment parsing starts, whether or not it succeeds. Drop it up front rather than
|
||||
// leaving indices pointing into a half overwritten array on an error return.
|
||||
retransmitState.reset();
|
||||
NakPduReader reader(rawPdu, pduSize, nakInfo);
|
||||
ReturnValue_t result = reader.parseData();
|
||||
// The segment requests may have been truncated, but whatever was parsed completely into the
|
||||
// array is still worth retransmitting: the receiver re-NAKs what it does not get.
|
||||
retransmitState.numSegments = nakInfo.getSegmentRequestsLen();
|
||||
retransmitState.currentIdx = 0;
|
||||
retransmitState.cursor = 0;
|
||||
retransmitState.metadataPending = false;
|
||||
// CFDP 5.2.6: a segment request of 0 to 0 asks for the metadata PDU rather than file data.
|
||||
// Compact it out of the list so the file data path does not have to special case it.
|
||||
size_t writeIdx = 0;
|
||||
for (size_t readIdx = 0; readIdx < retransmitState.numSegments; readIdx++) {
|
||||
const auto& segment = retransmitState.segments[readIdx];
|
||||
if (segment.first.value() == 0 and segment.second.value() == 0) {
|
||||
retransmitState.metadataPending = true;
|
||||
continue;
|
||||
}
|
||||
retransmitState.segments[writeIdx++] = segment;
|
||||
}
|
||||
retransmitState.numSegments = writeIdx;
|
||||
// Reported so a truncated NAK is visible as an error even though the requests which did parse
|
||||
// are serviced normally.
|
||||
return result;
|
||||
}
|
||||
|
||||
bool cfdp::SourceHandler::servicePendingRetransmissions(ReturnValue_t& result) {
|
||||
result = OK;
|
||||
if (retransmitState.metadataPending) {
|
||||
result = sendMetadataPdu();
|
||||
if (result == OK) {
|
||||
// Only clear the request once it actually went out. The peer asked for the metadata
|
||||
// because it cannot write the file at all without it, and it will not ask again until its
|
||||
// NAK timer expires.
|
||||
retransmitState.metadataPending = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const uint64_t fileSize = transactionParams.fileSize.value();
|
||||
while (retransmitState.currentIdx < retransmitState.numSegments) {
|
||||
const auto& segment = retransmitState.segments[retransmitState.currentIdx];
|
||||
uint64_t start = segment.first.value();
|
||||
uint64_t end = segment.second.value();
|
||||
if (retransmitState.cursor > start) {
|
||||
start = retransmitState.cursor;
|
||||
}
|
||||
if (end > fileSize) {
|
||||
end = fileSize;
|
||||
}
|
||||
if (start >= end) {
|
||||
retransmitState.currentIdx++;
|
||||
retransmitState.cursor = 0;
|
||||
continue;
|
||||
}
|
||||
uint64_t lenToSend = end - start;
|
||||
if (lenToSend > transactionParams.remoteCfg.maxFileSegmentLen) {
|
||||
lenToSend = transactionParams.remoteCfg.maxFileSegmentLen;
|
||||
}
|
||||
result = sendFileDataPdu(start, lenToSend);
|
||||
if (result != OK) {
|
||||
// The cursor must not move past data which was never enqueued for downlink, exactly as in
|
||||
// the forward-only path: the next call retries this same offset. Advancing here would drop
|
||||
// the segment until the peer's NAK timer re-requests it, and that costs a NAK limit credit.
|
||||
return true;
|
||||
}
|
||||
retransmitState.cursor = start + lenToSend;
|
||||
if (retransmitState.cursor >= end) {
|
||||
retransmitState.currentIdx++;
|
||||
retransmitState.cursor = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::SourceHandler::sendFileDataPdu(uint64_t offset, size_t lenToRead) {
|
||||
if (lenToRead > fileBuf.size()) {
|
||||
addError(FILE_SEGMENT_LEN_INVALID);
|
||||
return FILE_SEGMENT_LEN_INVALID;
|
||||
}
|
||||
size_t readLen = 0;
|
||||
ReturnValue_t result =
|
||||
sourceParams.user.vfs.readFromFile(transactionParams.sourceName.data(), offset, lenToRead,
|
||||
fileBuf.data(), readLen, fileBuf.size());
|
||||
if (result != returnvalue::OK) {
|
||||
addError(result);
|
||||
return result;
|
||||
}
|
||||
cfdp::Fss offsetFss(offset, transactionParams.pduConf.largeFile);
|
||||
auto fileDataInfo = FileDataInfo(offsetFss, fileBuf.data(), lenToRead);
|
||||
auto fileDataPdu = FileDataCreator(transactionParams.pduConf, fileDataInfo);
|
||||
return sendGenericPdu(PduType::FILE_DATA, std::nullopt, fileDataPdu);
|
||||
}
|
||||
|
||||
ReturnValue_t cfdp::SourceHandler::sendAckPdu(FileDirective ackedDirective,
|
||||
ConditionCode conditionCode) {
|
||||
// CFDP 5.2.4: the directive subtype code is 0b0001 for an acknowledged Finished PDU and
|
||||
// 0b0000 for every other acknowledged directive.
|
||||
AckInfo ackInfo(ackedDirective, conditionCode, AckTransactionStatus::ACTIVE,
|
||||
ackedDirective == FileDirective::FINISH ? 1 : 0);
|
||||
AckPduCreator ackPdu(ackInfo, transactionParams.pduConf);
|
||||
return sendGenericPdu(PduType::FILE_DIRECTIVE, FileDirective::ACK, ackPdu);
|
||||
}
|
||||
|
||||
void cfdp::SourceHandler::declareFault(ConditionCode code) {
|
||||
transactionParams.finishedConditionCode = code;
|
||||
transactionParams.finishedDeliveryCode = FileDeliveryCode::DATA_INCOMPLETE;
|
||||
sourceParams.cfg.fhBase.reportFault(transactionParams.id, code);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef FSFW_CFDP_CFDPSOURCEHANDLER_H
|
||||
#define FSFW_CFDP_CFDPSOURCEHANDLER_H
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
@@ -11,8 +12,10 @@
|
||||
#include "fsfw/cfdp/Fss.h"
|
||||
#include "fsfw/cfdp/handler/PutRequest.h"
|
||||
#include "fsfw/cfdp/handler/mib.h"
|
||||
#include "fsfw/cfdp/pdu/NakInfo.h"
|
||||
#include "fsfw/events/EventReportingProxyIF.h"
|
||||
#include "fsfw/storagemanager/StorageManagerIF.h"
|
||||
#include "fsfw/timemanager/Countdown.h"
|
||||
#include "fsfw/tmtcservices/AcceptsTelemetryIF.h"
|
||||
#include "fsfw/util/ProvidesSeqCountIF.h"
|
||||
|
||||
@@ -81,15 +84,52 @@ class SourceHandler {
|
||||
PduConfig pduConf;
|
||||
cfdp::TransactionId id{};
|
||||
|
||||
//! Number of positive ACK timer expirations for the EOF PDU, or of inactivity timer
|
||||
//! expirations while waiting for the Finished PDU.
|
||||
uint32_t positiveAckCounter = 0;
|
||||
bool finishedReceived = false;
|
||||
//! Delivery result reported by the peer in its Finished PDU. The defaults are what a
|
||||
//! transfer without closure reports, which is what the handler did unconditionally before
|
||||
//! the Finished PDU was actually parsed.
|
||||
ConditionCode finishedConditionCode = ConditionCode::NO_ERROR;
|
||||
FileDeliveryCode finishedDeliveryCode = FileDeliveryCode::DATA_COMPLETE;
|
||||
FileDeliveryStatus finishedDeliveryStatus = FileDeliveryStatus::FILE_STATUS_UNREPORTED;
|
||||
|
||||
void reset() {
|
||||
sourceNameSize = 0;
|
||||
destNameSize = 0;
|
||||
fileSize.setFileSize(0, false);
|
||||
progress = 0;
|
||||
closureRequested = false;
|
||||
positiveAckCounter = 0;
|
||||
finishedReceived = false;
|
||||
finishedConditionCode = ConditionCode::NO_ERROR;
|
||||
finishedDeliveryCode = FileDeliveryCode::DATA_COMPLETE;
|
||||
finishedDeliveryStatus = FileDeliveryStatus::FILE_STATUS_UNREPORTED;
|
||||
}
|
||||
} transactionParams;
|
||||
|
||||
//! Pending retransmissions requested by the last received NAK PDU. Only the most recent NAK is
|
||||
//! kept: it is the peer's authoritative statement about what is still missing, and bounding the
|
||||
//! state this way keeps a NAK storm from growing the handler's memory footprint.
|
||||
struct RetransmitState {
|
||||
static constexpr size_t MAX_SEGMENTS = 32;
|
||||
std::array<NakInfo::SegmentRequest, MAX_SEGMENTS> segments{};
|
||||
size_t numSegments = 0;
|
||||
size_t currentIdx = 0;
|
||||
//! Absolute file offset reached inside the segment at currentIdx, 0 if it was not started.
|
||||
uint64_t cursor = 0;
|
||||
bool metadataPending = false;
|
||||
|
||||
void reset() {
|
||||
numSegments = 0;
|
||||
currentIdx = 0;
|
||||
cursor = 0;
|
||||
metadataPending = false;
|
||||
}
|
||||
[[nodiscard]] bool empty() const { return not metadataPending and currentIdx >= numSegments; }
|
||||
} retransmitState;
|
||||
|
||||
PduSenderIF& pduSender;
|
||||
std::vector<uint8_t> pduBuf;
|
||||
cfdp::CfdpState state = cfdp::CfdpState::IDLE;
|
||||
@@ -98,13 +138,29 @@ class SourceHandler {
|
||||
SourceHandlerParams sourceParams;
|
||||
cfdp::FsfwParams fsfwParams;
|
||||
FsmResult fsmResult;
|
||||
//! Guards the EOF PDU in acknowledged mode and the wait for the Finished PDU in both modes.
|
||||
Countdown positiveAckTimer;
|
||||
|
||||
FsmResult& fsmNacked();
|
||||
FsmResult& fsmNacked(std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket);
|
||||
FsmResult& fsmAcked(std::optional<std::reference_wrapper<PduPacketIF>> optPduPacket);
|
||||
//! True if the PDU's source entity ID and sequence number match the running transaction. The
|
||||
//! CFDP handler routes PDUs by direction only, so this is the only transaction level filter.
|
||||
[[nodiscard]] bool pduBelongsToTransaction(const PduPacketIF& pduPacket) const;
|
||||
void handleAckedPdu(PduPacketIF& pduPacket);
|
||||
ReturnValue_t handleFinishedPdu(const PduPacketIF& pduPacket);
|
||||
ReturnValue_t handleAckPdu(const PduPacketIF& pduPacket);
|
||||
ReturnValue_t handleNakPdu(const PduPacketIF& pduPacket);
|
||||
//! Sends one PDU of the outstanding retransmissions, if there are any.
|
||||
bool servicePendingRetransmissions(ReturnValue_t& result);
|
||||
ReturnValue_t sendAckPdu(FileDirective ackedDirective, ConditionCode conditionCode);
|
||||
ReturnValue_t checksumGeneration();
|
||||
ReturnValue_t sendMetadataPdu();
|
||||
ReturnValue_t prepareAndSendMetadataPdu();
|
||||
ReturnValue_t sendFileDataPdu(uint64_t offset, size_t lenToRead);
|
||||
ReturnValue_t prepareAndSendNextFileDataPdu(bool& noFileDataPdu);
|
||||
ReturnValue_t prepareAndSendEofPdu();
|
||||
ReturnValue_t noticeOfCompletion();
|
||||
void declareFault(ConditionCode code);
|
||||
ReturnValue_t reset();
|
||||
|
||||
[[nodiscard]] ReturnValue_t sendGenericPdu(PduType pduType,
|
||||
|
||||
@@ -19,13 +19,13 @@ struct FsfwParams {
|
||||
};
|
||||
|
||||
namespace events {
|
||||
static constexpr Event PDU_SEND_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);
|
||||
static constexpr Event PDU_SEND_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);
|
||||
static constexpr Event FILENAME_TOO_LARGE_ERROR = event::makeEvent<SSID, 4, severity::LOW>();
|
||||
//! [EXPORT] : [COMMENT] CFDP request handling failed. P2: Returncode.
|
||||
static constexpr Event HANDLING_CFDP_REQUEST_FAILED = event::makeEvent(SSID, 5, severity::LOW);
|
||||
static constexpr Event HANDLING_CFDP_REQUEST_FAILED = event::makeEvent<SSID, 5, severity::LOW>();
|
||||
} // namespace events
|
||||
|
||||
static constexpr ReturnValue_t SOURCE_TRANSACTION_PENDING = returnvalue::makeCode(CID, 0);
|
||||
@@ -38,4 +38,6 @@ static constexpr ReturnValue_t TARGET_MSG_QUEUE_FULL = returnvalue::makeCode(CID
|
||||
static constexpr ReturnValue_t TM_STORE_FULL = returnvalue::makeCode(CID, 7);
|
||||
static constexpr ReturnValue_t DEST_NON_METADATA_PDU_AS_FIRST_PDU = returnvalue::makeCode(CID, 8);
|
||||
static constexpr ReturnValue_t PDU_BUFFER_TOO_SMALL = returnvalue::makeCode(CID, 9);
|
||||
//! The resolved transmission mode of a request is not supported by this handler (yet).
|
||||
static constexpr ReturnValue_t TRANSMISSION_MODE_NOT_SUPPORTED = returnvalue::makeCode(CID, 10);
|
||||
} // namespace cfdp
|
||||
@@ -36,6 +36,31 @@ struct RemoteEntityCfg {
|
||||
TransmissionMode defaultTransmissionMode = TransmissionMode::UNACKNOWLEDGED;
|
||||
ChecksumType defaultChecksum = ChecksumType::NULL_CHECKSUM;
|
||||
uint8_t version = CFDP_VERSION_2;
|
||||
|
||||
// Acknowledged mode (class 2) parameters. The names mirror cfdppy.mib.RemoteEntityConfig field
|
||||
// for field so both ends of a link can be configured from the same set of numbers. The defaults
|
||||
// are inert for class 1, which uses none of them.
|
||||
|
||||
//! Interval of the positive acknowledgment timer, which guards EOF (source side) and Finished
|
||||
//! (destination side) PDUs. Must be larger than the peer's worst case time to answer with the
|
||||
//! matching ACK PDU, or both sides retransmit over each other.
|
||||
uint32_t positiveAckTimerIntervalMs = 10000;
|
||||
//! Number of positive ACK timer expirations after which POSITIVE_ACK_LIMIT_REACHED is declared.
|
||||
uint32_t positiveAckTimerExpirationLimit = 2;
|
||||
//! Interval of the NAK timer used by the deferred lost segment procedure.
|
||||
uint32_t nakTimerIntervalMs = 10000;
|
||||
//! Number of NAK timer expirations after which NAK_LIMIT_REACHED is declared.
|
||||
uint32_t nakTimerExpirationLimit = 2;
|
||||
//! If true, a NAK is issued as soon as a gap is detected. If false, the deferred procedure is
|
||||
//! used and the NAK sequence is only issued once the EOF PDU has arrived. Deferred is the
|
||||
//! default: on a lossy link the immediate procedure produces a burst of NAK PDUs in the
|
||||
//! opposite direction exactly when the link is already struggling.
|
||||
bool immediateNakMode = false;
|
||||
//! Number of check timer expirations after EOF reception after which CHECK_LIMIT_REACHED is
|
||||
//! declared and an incomplete transaction is cancelled instead of pinning the handler.
|
||||
uint32_t checkLimit = 2;
|
||||
//! Interval of the check timer, see checkLimit.
|
||||
uint32_t checkTimerIntervalMs = 10000;
|
||||
};
|
||||
|
||||
} // namespace cfdp
|
||||
|
||||
@@ -23,6 +23,9 @@ class AckPduCreator : public FileDirectiveCreator {
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const override;
|
||||
|
||||
//! Un-hide the convenience overloads of the base class, same as FinishedPduCreator does.
|
||||
using FileDirectiveCreator::serialize;
|
||||
|
||||
private:
|
||||
AckInfo& ackInfo;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,15 @@ ReturnValue_t FinishPduReader::parseData() {
|
||||
size_t currentIdx = FileDirectiveReader::getHeaderSize();
|
||||
const uint8_t* buf = pointers.rawPtr + currentIdx;
|
||||
size_t remSize = FileDirectiveReader::getWholePduSize() - currentIdx;
|
||||
// Drop the PDU CRC from the parsed range before parseTlvs below, which runs until the range is
|
||||
// exhausted. Without this a Finished PDU carrying a CRC is rejected as an invalid TLV type -
|
||||
// including the common NO_ERROR case, where the CRC is the only thing left after the first byte.
|
||||
if (getCrcFlag()) {
|
||||
if (remSize < 2) {
|
||||
return SerializeIF::STREAM_TOO_SHORT;
|
||||
}
|
||||
remSize -= 2;
|
||||
}
|
||||
if (remSize < 1) {
|
||||
return SerializeIF::STREAM_TOO_SHORT;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ class KeepAlivePduCreator : public FileDirectiveCreator {
|
||||
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const override;
|
||||
using FileDirectiveCreator::serialize;
|
||||
|
||||
private:
|
||||
cfdp::Fss& progress;
|
||||
|
||||
@@ -17,6 +17,15 @@ ReturnValue_t MetadataPduReader::parseData() {
|
||||
size_t currentIdx = FileDirectiveReader::getHeaderSize();
|
||||
const uint8_t* buf = pointers.rawPtr + currentIdx;
|
||||
size_t remSize = FileDirectiveReader::getWholePduSize() - currentIdx;
|
||||
// The PDU CRC occupies the last two bytes of the PDU. Take it out of the parsed range up front,
|
||||
// like FileDataReader does: the option loop below consumes bytes until the range is exhausted,
|
||||
// so a CRC left in place would be deserialized as another TLV and rejected as an invalid type.
|
||||
if (getCrcFlag()) {
|
||||
if (remSize < 2) {
|
||||
return SerializeIF::STREAM_TOO_SHORT;
|
||||
}
|
||||
remSize -= 2;
|
||||
}
|
||||
if (remSize < 1) {
|
||||
return SerializeIF::STREAM_TOO_SHORT;
|
||||
}
|
||||
@@ -38,10 +47,6 @@ ReturnValue_t MetadataPduReader::parseData() {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (getCrcFlag() && remSize == 2) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
if (remSize > 0) {
|
||||
if (optionArrayMaxSize == 0 or optionArray == nullptr) {
|
||||
return cfdp::METADATA_CANT_PARSE_OPTIONS;
|
||||
|
||||
@@ -25,6 +25,9 @@ class NakPduCreator : public FileDirectiveCreator {
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const override;
|
||||
|
||||
//! Un-hide the convenience overloads of the base class, same as FinishedPduCreator does.
|
||||
using FileDirectiveCreator::serialize;
|
||||
|
||||
/**
|
||||
* If you change the info struct, you might need to update the directive field length
|
||||
* manually
|
||||
|
||||
@@ -11,6 +11,14 @@ ReturnValue_t NakPduReader::parseData() {
|
||||
size_t currentIdx = FileDirectiveReader::getHeaderSize();
|
||||
const uint8_t* buffer = pointers.rawPtr + currentIdx;
|
||||
size_t remSize = FileDirectiveReader::getWholePduSize() - currentIdx;
|
||||
// Drop the PDU CRC from the parsed range before the segment request loop below, which runs
|
||||
// until the range is exhausted and would otherwise read the CRC as a truncated segment request.
|
||||
if (getCrcFlag()) {
|
||||
if (remSize < 2) {
|
||||
return SerializeIF::STREAM_TOO_SHORT;
|
||||
}
|
||||
remSize -= 2;
|
||||
}
|
||||
if (remSize < 1) {
|
||||
return SerializeIF::STREAM_TOO_SHORT;
|
||||
}
|
||||
@@ -34,17 +42,24 @@ ReturnValue_t NakPduReader::parseData() {
|
||||
if (segReqs != nullptr) {
|
||||
size_t idx = 0;
|
||||
while (remSize > 0) {
|
||||
// Every early return below reports the number of *complete* segment requests written so
|
||||
// far. Leaving the length at 0 would make a partially parsed NAK indistinguishable from
|
||||
// an empty one, and a caller which tolerates the error code would then act on nothing.
|
||||
if (idx == maxSegReqs) {
|
||||
nakInfo.setSegmentRequestLen(idx);
|
||||
return cfdp::NAK_CANT_PARSE_OPTIONS;
|
||||
}
|
||||
result =
|
||||
segReqs[idx].first.deSerialize(&buffer, &remSize, SerializeIF::Endianness::NETWORK);
|
||||
if (result != returnvalue::OK) {
|
||||
nakInfo.setSegmentRequestLen(idx);
|
||||
return result;
|
||||
}
|
||||
result =
|
||||
segReqs[idx].second.deSerialize(&buffer, &remSize, SerializeIF::Endianness::NETWORK);
|
||||
if (result != returnvalue::OK) {
|
||||
// The entry at idx is half written, so it is not counted.
|
||||
nakInfo.setSegmentRequestLen(idx);
|
||||
return result;
|
||||
}
|
||||
idx++;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
ExtendedControllerBase::ExtendedControllerBase(object_id_t objectId, size_t commandQueueDepth)
|
||||
: ControllerBase(objectId, commandQueueDepth),
|
||||
poolManager(this, commandQueue),
|
||||
hkHelper(this, commandQueue),
|
||||
actionHelper(this, commandQueue) {}
|
||||
|
||||
ExtendedControllerBase::~ExtendedControllerBase() = default;
|
||||
@@ -10,22 +10,18 @@ ExtendedControllerBase::~ExtendedControllerBase() = default;
|
||||
ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId,
|
||||
MessageQueueId_t commandedBy,
|
||||
const uint8_t *data, size_t size) {
|
||||
/* Needs to be overriden and implemented by child class. */
|
||||
// Needs to be overriden and implemented by child class.
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
object_id_t ExtendedControllerBase::getObjectId() const { return SystemObject::getObjectId(); }
|
||||
|
||||
uint32_t ExtendedControllerBase::getPeriodicOperationFrequency() const {
|
||||
return this->executingTask->getPeriodMs();
|
||||
}
|
||||
|
||||
ReturnValue_t ExtendedControllerBase::handleCommandMessage(CommandMessage *message) {
|
||||
ReturnValue_t result = actionHelper.handleActionMessage(message);
|
||||
if (result == returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
return poolManager.handleHousekeepingMessage(message);
|
||||
return hkHelper.handleHousekeepingMessage(message);
|
||||
}
|
||||
|
||||
void ExtendedControllerBase::handleQueue() {
|
||||
@@ -48,7 +44,7 @@ void ExtendedControllerBase::handleQueue() {
|
||||
continue;
|
||||
}
|
||||
|
||||
result = poolManager.handleHousekeepingMessage(&command);
|
||||
result = hkHelper.handleHousekeepingMessage(&command);
|
||||
if (result == returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
@@ -72,22 +68,18 @@ ReturnValue_t ExtendedControllerBase::initialize() {
|
||||
return result;
|
||||
}
|
||||
|
||||
return poolManager.initialize(commandQueue);
|
||||
}
|
||||
|
||||
ReturnValue_t ExtendedControllerBase::initializeAfterTaskCreation() {
|
||||
return poolManager.initializeAfterTaskCreation();
|
||||
return hkHelper.initialize(commandQueue);
|
||||
}
|
||||
|
||||
ReturnValue_t ExtendedControllerBase::performOperation(uint8_t opCode) {
|
||||
handleQueue();
|
||||
performControlOperation(opCode);
|
||||
/* We do this after performing control operation because variables will be set changed
|
||||
in this function. */
|
||||
poolManager.performHkOperation();
|
||||
// We do this after performing control operation because variables will be set changed
|
||||
// in this function.
|
||||
hkHelper.performHkOperation();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
MessageQueueId_t ExtendedControllerBase::getCommandQueue() const { return commandQueue->getId(); }
|
||||
|
||||
LocalDataPoolManager *ExtendedControllerBase::getHkManagerHandle() { return &poolManager; }
|
||||
datapool::SharedPool *ExtendedControllerBase::getOptionalSharedPool() { return nullptr; }
|
||||
@@ -1,10 +1,11 @@
|
||||
#ifndef FSFW_CONTROLLER_EXTENDEDCONTROLLERBASE_H_
|
||||
#define FSFW_CONTROLLER_EXTENDEDCONTROLLERBASE_H_
|
||||
|
||||
#include <fsfw/housekeeping/GeneratesPeriodicHkIF.h>
|
||||
|
||||
#include "ControllerBase.h"
|
||||
#include "fsfw/action.h"
|
||||
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
|
||||
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
|
||||
#include "fsfw/housekeeping/PeriodicHkHelper.h"
|
||||
|
||||
/**
|
||||
* @brief Extends the basic ControllerBase with commonly used components
|
||||
@@ -15,9 +16,9 @@
|
||||
*/
|
||||
class ExtendedControllerBase : public ControllerBase,
|
||||
public HasActionsIF,
|
||||
public HasLocalDataPoolIF {
|
||||
public hk::GeneratesPeriodicHkIF {
|
||||
public:
|
||||
ExtendedControllerBase(object_id_t objectId, size_t commandQueueDepth = 3);
|
||||
explicit ExtendedControllerBase(object_id_t objectId, size_t commandQueueDepth = 3);
|
||||
~ExtendedControllerBase() override;
|
||||
|
||||
/* SystemObjectIF overrides */
|
||||
@@ -27,12 +28,19 @@ class ExtendedControllerBase : public ControllerBase,
|
||||
|
||||
/* ExecutableObjectIF overrides */
|
||||
ReturnValue_t performOperation(uint8_t opCode) override;
|
||||
ReturnValue_t initializeAfterTaskCreation() override;
|
||||
|
||||
protected:
|
||||
LocalDataPoolManager poolManager;
|
||||
hk::PeriodicHelper hkHelper;
|
||||
ActionHelper actionHelper;
|
||||
|
||||
// Periodic HK methods, default method assumes that no shared pool is required.
|
||||
datapool::SharedPool* getOptionalSharedPool() override = 0;
|
||||
|
||||
// Periodic HK abstract methods.
|
||||
ReturnValue_t serializeHkDataset(dp::sid_t structureId, uint8_t* buf,
|
||||
size_t maxSize) override = 0;
|
||||
ReturnValue_t specifyHkDatasets(std::vector<hk::SetSpecification>& setList) override = 0;
|
||||
|
||||
/**
|
||||
* Implemented by child class. Handle all command messages which are
|
||||
* not health, mode, action or housekeeping messages.
|
||||
@@ -49,18 +57,12 @@ class ExtendedControllerBase : public ControllerBase,
|
||||
// Handle the four messages mentioned above
|
||||
void handleQueue() override;
|
||||
|
||||
/* HasActionsIF overrides */
|
||||
// HasActionsIF overrides
|
||||
ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
|
||||
const uint8_t* data, size_t size) override;
|
||||
|
||||
/* HasLocalDatapoolIF overrides */
|
||||
LocalDataPoolManager* getHkManagerHandle() override;
|
||||
// HasLocalDatapoolIF overrides
|
||||
[[nodiscard]] object_id_t getObjectId() const override;
|
||||
[[nodiscard]] uint32_t getPeriodicOperationFrequency() const override;
|
||||
|
||||
ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) override = 0;
|
||||
LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override = 0;
|
||||
|
||||
// Mode abstract functions
|
||||
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||
#define FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||
|
||||
// Collected related headers
|
||||
#include "fsfw/datapool/LocalPoolVector.h"
|
||||
#include "fsfw/datapool/PoolVariable.h"
|
||||
#include "fsfw/datapool/SharedPool.h"
|
||||
#include "fsfw/datapool/SharedSet.h"
|
||||
#include "fsfw/datapool/StaticSharedSet.h"
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ */
|
||||
@@ -1 +1,6 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE PoolDataSetBase.cpp PoolEntry.cpp)
|
||||
target_sources(
|
||||
${LIB_FSFW_NAME}
|
||||
PRIVATE PoolDataSetBase.cpp PoolEntry.cpp SharedPool.cpp SharedSet.cpp
|
||||
SharedSetBase.cpp LocalPoolObjectBase.cpp)
|
||||
|
||||
add_subdirectory(internal)
|
||||
+29
-40
@@ -1,37 +1,26 @@
|
||||
#include "fsfw/datapoollocal/LocalPoolObjectBase.h"
|
||||
#include "fsfw/datapool/LocalPoolObjectBase.h"
|
||||
|
||||
#include "fsfw/datapoollocal/AccessLocalPoolF.h"
|
||||
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
|
||||
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
|
||||
#include "fsfw/housekeeping/PeriodicHkHelper.h"
|
||||
#include "fsfw/objectmanager/ObjectManager.h"
|
||||
#include "internal/HasLocalDpIFUserAttorney.h"
|
||||
|
||||
LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkOwner,
|
||||
DataSetIF* dataSet, pool_rwm_t setReadWriteMode)
|
||||
: localPoolId(poolId), readWriteMode(setReadWriteMode) {
|
||||
using namespace dp;
|
||||
PoolObjectBase::PoolObjectBase(SharedPool& sharedPool, dp::id_t poolId, DataSetIF* dataSet,
|
||||
pool_rwm_t setReadWriteMode)
|
||||
: localPoolId(poolId), readWriteMode(setReadWriteMode), sharedPool(&sharedPool) {
|
||||
if (poolId == PoolVariableIF::NO_PARAMETER) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "LocalPoolVar<T>::LocalPoolVar: 0 passed as pool ID, "
|
||||
<< "which is the NO_PARAMETER value!" << std::endl;
|
||||
#endif
|
||||
}
|
||||
if (hkOwner == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "LocalPoolVar<T>::LocalPoolVar: The supplied pool "
|
||||
<< "owner is a invalid!" << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
AccessPoolManagerIF* poolManAccessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
|
||||
hkManager = poolManAccessor->getPoolManagerHandle();
|
||||
|
||||
if (dataSet != nullptr) {
|
||||
dataSet->registerVariable(this);
|
||||
}
|
||||
}
|
||||
|
||||
LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet,
|
||||
pool_rwm_t setReadWriteMode)
|
||||
PoolObjectBase::PoolObjectBase(object_id_t poolOwner, id_t poolId, DataSetIF* dataSet,
|
||||
pool_rwm_t setReadWriteMode)
|
||||
: localPoolId(poolId), readWriteMode(setReadWriteMode) {
|
||||
if (poolId == PoolVariableIF::NO_PARAMETER) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
@@ -44,7 +33,7 @@ LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId,
|
||||
"which is the NO_PARAMETER value!\n");
|
||||
#endif
|
||||
}
|
||||
HasLocalDataPoolIF* hkOwner = ObjectManager::instance()->get<HasLocalDataPoolIF>(poolOwner);
|
||||
auto* hkOwner = ObjectManager::instance()->get<hk::GeneratesPeriodicHkIF>(poolOwner);
|
||||
if (hkOwner == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "LocalPoolVariable: The supplied pool owner 0x" << std::hex << poolOwner
|
||||
@@ -58,10 +47,14 @@ LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId,
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
AccessPoolManagerIF* accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
|
||||
if (accessor != nullptr) {
|
||||
hkManager = accessor->getPoolManagerHandle();
|
||||
sharedPool = hkOwner->getOptionalSharedPool();
|
||||
if (sharedPool == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "PoolObjectBase: HK owner 0x" << std::hex << poolOwner << std::dec
|
||||
<< "does not have a shared pool " << std::endl;
|
||||
#else
|
||||
sif::printError("PoolObjectBase: HK owner 0x%08x does not have a shared pool\n", poolOwner);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (dataSet != nullptr) {
|
||||
@@ -69,26 +62,22 @@ LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId,
|
||||
}
|
||||
}
|
||||
|
||||
pool_rwm_t LocalPoolObjectBase::getReadWriteMode() const { return readWriteMode; }
|
||||
pool_rwm_t PoolObjectBase ::getReadWriteMode() const { return readWriteMode; }
|
||||
|
||||
bool LocalPoolObjectBase::isValid() const { return valid; }
|
||||
id_t PoolObjectBase ::getDataPoolId() const { return localPoolId; }
|
||||
|
||||
void LocalPoolObjectBase::setValid(bool valid) { this->valid = valid; }
|
||||
void PoolObjectBase::setDataPoolId(id_t poolId) { this->localPoolId = poolId; }
|
||||
|
||||
lp_id_t LocalPoolObjectBase::getDataPoolId() const { return localPoolId; }
|
||||
|
||||
void LocalPoolObjectBase::setDataPoolId(lp_id_t poolId) { this->localPoolId = poolId; }
|
||||
|
||||
void LocalPoolObjectBase::setChanged(bool changed) { this->changed = changed; }
|
||||
|
||||
bool LocalPoolObjectBase::hasChanged() const { return changed; }
|
||||
|
||||
void LocalPoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) {
|
||||
void PoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) {
|
||||
this->readWriteMode = newReadWriteMode;
|
||||
}
|
||||
|
||||
void LocalPoolObjectBase::reportReadCommitError(const char* variableType, ReturnValue_t error,
|
||||
bool read, object_id_t objectId, lp_id_t lpId) {
|
||||
[[nodiscard]] bool PoolObjectBase::isValid() const { return valid; }
|
||||
|
||||
void PoolObjectBase::setValid(bool valid) { this->valid = valid; }
|
||||
|
||||
void PoolObjectBase::reportReadCommitError(const char* variableType, ReturnValue_t error, bool read,
|
||||
object_id_t objectId, id_t lpId) {
|
||||
#if FSFW_DISABLE_PRINTOUT == 0
|
||||
const char* variablePrintout = variableType;
|
||||
if (variablePrintout == nullptr) {
|
||||
@@ -102,9 +91,9 @@ void LocalPoolObjectBase::reportReadCommitError(const char* variableType, Return
|
||||
}
|
||||
|
||||
const char* errMsg = nullptr;
|
||||
if (error == localpool::POOL_ENTRY_NOT_FOUND) {
|
||||
if (error == POOL_ENTRY_NOT_FOUND) {
|
||||
errMsg = "Pool entry not found";
|
||||
} else if (error == localpool::POOL_ENTRY_TYPE_CONFLICT) {
|
||||
} else if (error == POOL_ENTRY_TYPE_CONFLICT) {
|
||||
errMsg = "Pool entry type conflict";
|
||||
} else if (error == PoolVariableIF::INVALID_READ_WRITE_MODE) {
|
||||
errMsg = "Pool variable wrong read-write mode";
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "SharedPool.h"
|
||||
#include "fsfw/datapool/PoolVariableIF.h"
|
||||
#include "fsfw/datapool/definitions.h"
|
||||
#include "fsfw/objectmanager/SystemObjectIF.h"
|
||||
#include "fsfw/returnvalues/returnvalue.h"
|
||||
|
||||
class DataSetIF;
|
||||
|
||||
namespace datapool {
|
||||
/**
|
||||
* @brief This class serves as a non-template base for pool objects like pool variables
|
||||
* or pool vectors.
|
||||
*/
|
||||
class PoolObjectBase : public PoolVariableIF {
|
||||
public:
|
||||
PoolObjectBase(dp::SharedPool& sharedPool, dp::id_t poolId, DataSetIF* dataSet,
|
||||
pool_rwm_t setReadWriteMode);
|
||||
|
||||
PoolObjectBase(object_id_t poolOwner, dp::id_t poolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
|
||||
|
||||
void setReadWriteMode(pool_rwm_t newReadWriteMode) override;
|
||||
[[nodiscard]] pool_rwm_t getReadWriteMode() const override;
|
||||
|
||||
[[nodiscard]] dp::id_t getDataPoolId() const override;
|
||||
void setDataPoolId(dp::id_t poolId);
|
||||
|
||||
[[nodiscard]] bool isValid() const;
|
||||
void setValid(bool valid);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief To access the correct data pool entry on read and commit calls,
|
||||
* the data pool id is stored.
|
||||
*/
|
||||
uint32_t localPoolId = PoolVariableIF::NO_PARAMETER;
|
||||
|
||||
/**
|
||||
* @brief The information whether the class is read-write or
|
||||
* read-only is stored here.
|
||||
*/
|
||||
ReadWriteMode_t readWriteMode = pool_rwm_t::VAR_READ_WRITE;
|
||||
|
||||
bool valid = false;
|
||||
|
||||
//! @brief Pointer to the class which manages the HK pool.
|
||||
dp::SharedPool* sharedPool = nullptr;
|
||||
|
||||
void reportReadCommitError(const char* variableType, ReturnValue_t error, bool read,
|
||||
object_id_t objectId, dp::id_t lpId);
|
||||
};
|
||||
|
||||
} // namespace datapool
|
||||
@@ -1,14 +1,15 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_
|
||||
#define FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/ipc/MutexGuard.h>
|
||||
|
||||
#include "../datapool/DataSetIF.h"
|
||||
#include "../datapool/PoolEntry.h"
|
||||
#include "../datapool/PoolVariableIF.h"
|
||||
#include "../datapoollocal/LocalDataPoolManager.h"
|
||||
#include "../serialize/SerializeAdapter.h"
|
||||
#include "../serviceinterface/ServiceInterface.h"
|
||||
#include "LocalPoolObjectBase.h"
|
||||
#include "internal/LocalDpManagerAttorney.h"
|
||||
#include "fsfw/datapool/DataSetIF.h"
|
||||
#include "fsfw/datapool/PoolEntry.h"
|
||||
#include "fsfw/datapool/PoolVariableIF.h"
|
||||
#include "fsfw/serialize/SerializeAdapter.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
namespace datapool {
|
||||
|
||||
/**
|
||||
* @brief This is the access class for array-type data pool entries.
|
||||
@@ -24,16 +25,16 @@
|
||||
* @tparam T
|
||||
* This template parameter specifies the data type of an array entry. Currently,
|
||||
* all plain data types are supported, but in principle any type is possible.
|
||||
* @tparam vector_size
|
||||
* @tparam N
|
||||
* This template parameter specifies the vector size of this entry. Using a
|
||||
* template parameter for this is not perfect, but avoids
|
||||
* dynamic memory allocation.
|
||||
* @ingroup data_pool
|
||||
*/
|
||||
template <typename T, uint16_t vectorSize>
|
||||
class LocalPoolVector : public LocalPoolObjectBase {
|
||||
template <typename T, size_t N>
|
||||
class PoolVector : public PoolObjectBase {
|
||||
public:
|
||||
LocalPoolVector() = delete;
|
||||
PoolVector() = delete;
|
||||
/**
|
||||
* This constructor is used by the data creators to have pool variable
|
||||
* instances which can also be stored in datasets.
|
||||
@@ -47,8 +48,9 @@ class LocalPoolVector : public LocalPoolObjectBase {
|
||||
* @param dataSet The data set in which the variable shall register itself.
|
||||
* If nullptr, the variable is not registered.
|
||||
*/
|
||||
LocalPoolVector(HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
|
||||
PoolVector(SharedPool& sharedPool, id_t poolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE)
|
||||
: PoolObjectBase(sharedPool, poolId, dataSet, setReadWriteMode) {}
|
||||
|
||||
/**
|
||||
* This constructor is used by data users like controllers to have
|
||||
@@ -57,15 +59,15 @@ class LocalPoolVector : public LocalPoolObjectBase {
|
||||
* It does not fetch the current value from the data pool. This is performed
|
||||
* by the read() operation (which is not thread-safe).
|
||||
* Datasets can be used to access local pool entires in a thread-safe way.
|
||||
* @param poolOwner Owner of the shared pool.
|
||||
* @param poolId ID of the local pool entry.
|
||||
* @param hkOwner Pointer of the owner. This will generally be the calling
|
||||
* class itself which passes "this".
|
||||
* @param setReadWriteMode Specify the read-write mode of the pool variable.
|
||||
* @param dataSet The data set in which the variable shall register itself.
|
||||
* If nullptr, the variable is not registered.
|
||||
*/
|
||||
LocalPoolVector(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
|
||||
PoolVector(object_id_t poolOwner, id_t poolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE)
|
||||
: PoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
|
||||
/**
|
||||
* Variation which takes the unique global identifier of a local pool
|
||||
* vector.
|
||||
@@ -73,8 +75,10 @@ class LocalPoolVector : public LocalPoolObjectBase {
|
||||
* @param dataSet
|
||||
* @param setReadWriteMode
|
||||
*/
|
||||
LocalPoolVector(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
|
||||
PoolVector(g_id_t globalPoolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE)
|
||||
: PoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, dataSet, setReadWriteMode) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This is the local copy of the data pool entry.
|
||||
@@ -82,27 +86,80 @@ class LocalPoolVector : public LocalPoolObjectBase {
|
||||
* The user can work on this attribute just like he would on a local
|
||||
* array of this type.
|
||||
*/
|
||||
T value[vectorSize] = {};
|
||||
T value[N] = {};
|
||||
/**
|
||||
* @brief The classes destructor is empty.
|
||||
* @details If commit() was not called, the local value is
|
||||
* discarded and not written back to the data pool.
|
||||
*/
|
||||
~LocalPoolVector() {};
|
||||
~PoolVector() override {};
|
||||
/**
|
||||
* @brief The operation returns the number of array entries
|
||||
* in this variable.
|
||||
*/
|
||||
uint8_t getSize() { return vectorSize; }
|
||||
size_t getSize() { return N; }
|
||||
|
||||
T& operator[](size_t i);
|
||||
const T& operator[](size_t i) const;
|
||||
T& operator[](size_t i) {
|
||||
if (i < N) {
|
||||
return value[i];
|
||||
}
|
||||
// If this happens, I have to set some value. I consider this
|
||||
// a configuration error, but I wont exit here.
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "PoolVector: Invalid index. Setting or returning"
|
||||
" last value!"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning(
|
||||
"PoolVector: Invalid index. Setting or returning"
|
||||
" last value!\n");
|
||||
#endif
|
||||
return value[N - 1];
|
||||
}
|
||||
const T& operator[](size_t i) const {
|
||||
if (i < N) {
|
||||
return value[i];
|
||||
}
|
||||
// If this happens, I have to set some value. I consider this
|
||||
// a configuration error, but I wont exit here.
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "PoolVector: Invalid index. Setting or returning"
|
||||
" last value!"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning(
|
||||
"PoolVector: Invalid index. Setting or returning"
|
||||
" last value!\n");
|
||||
#endif
|
||||
return value[N - 1];
|
||||
}
|
||||
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
|
||||
SerializeIF::Endianness streamEndiannes) const override;
|
||||
size_t getSerializedSize() const override;
|
||||
SerializeIF::Endianness streamEndianness) const override {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
for (uint16_t i = 0; i < N; i++) {
|
||||
result = SerializeAdapter::serialize(&(value[i]), buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t getSerializedSize() const override {
|
||||
return N * SerializeAdapter::getSerializedSize(value);
|
||||
}
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) override;
|
||||
SerializeIF::Endianness streamEndianness) override {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
for (uint16_t i = 0; i < N; i++) {
|
||||
result = SerializeAdapter::deSerialize(&(value[i]), buffer, size, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This is a call to read the array's values
|
||||
@@ -118,7 +175,10 @@ class LocalPoolVector : public LocalPoolObjectBase {
|
||||
* at once to avoid the overhead of unnecessary lock und unlock operations.
|
||||
*/
|
||||
ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20) override;
|
||||
uint32_t timeoutMs = 20) override {
|
||||
MutexGuard mg(sharedPool->getPoolMutex(), timeoutType, timeoutMs);
|
||||
return readWithoutLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The commit call copies the array values back to the data pool.
|
||||
@@ -129,15 +189,11 @@ class LocalPoolVector : public LocalPoolObjectBase {
|
||||
* It is recommended to use DataSets to read and commit multiple variables
|
||||
* at once to avoid the overhead of unnecessary lock und unlock operations.
|
||||
*/
|
||||
ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20) override;
|
||||
|
||||
/**
|
||||
* @brief This commit call also sets the validity of the pool entry.
|
||||
* @details
|
||||
*/
|
||||
ReturnValue_t commit(bool valid, MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20);
|
||||
ReturnValue_t commit(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) override {
|
||||
MutexGuard mg(sharedPool->getPoolMutex(), timeoutType, timeoutMs);
|
||||
return commitWithoutLock();
|
||||
}
|
||||
ReturnValue_t commit() { return commit(MutexIF::TimeoutType::WAITING, 20); }
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -148,7 +204,23 @@ class LocalPoolVector : public LocalPoolObjectBase {
|
||||
* of consecutive lock und unlock operations.
|
||||
* Declared protected to discourage free public usage.
|
||||
*/
|
||||
ReturnValue_t readWithoutLock() override;
|
||||
ReturnValue_t readWithoutLock() override {
|
||||
if (readWriteMode == pool_rwm_t::VAR_WRITE) {
|
||||
return PoolVariableIF::INVALID_READ_WRITE_MODE;
|
||||
}
|
||||
|
||||
PoolEntry<T>* poolEntry = nullptr;
|
||||
ReturnValue_t result = sharedPool->fetchPoolEntry(localPoolId, &poolEntry);
|
||||
memset(this->value, 0, N * sizeof(T));
|
||||
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
this->valid = poolEntry->getValid();
|
||||
std::memcpy(this->value, poolEntry->getDataPtr(), poolEntry->getByteSize());
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Like #commit, but without a lock protection of the global pool.
|
||||
* @details
|
||||
@@ -157,19 +229,37 @@ class LocalPoolVector : public LocalPoolObjectBase {
|
||||
* of consecutive lock und unlock operations.
|
||||
* Declared protected to discourage free public usage.
|
||||
*/
|
||||
ReturnValue_t commitWithoutLock() override;
|
||||
ReturnValue_t commitWithoutLock() override {
|
||||
if (readWriteMode == pool_rwm_t::VAR_READ) {
|
||||
return PoolVariableIF::INVALID_READ_WRITE_MODE;
|
||||
}
|
||||
PoolEntry<T>* poolEntry = nullptr;
|
||||
ReturnValue_t result = sharedPool->fetchPoolEntry(localPoolId, &poolEntry);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
poolEntry->setValid(this->valid);
|
||||
std::memcpy(poolEntry->getDataPtr(), this->value, poolEntry->getByteSize());
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
private:
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
// std::ostream is the type for object std::cout
|
||||
template <typename U, uint16_t otherSize>
|
||||
friend std::ostream& operator<<(std::ostream& out, const LocalPoolVector<U, otherSize>& var);
|
||||
std::ostream& operator<<(std::ostream& out, const PoolVector<T, N>& var) {
|
||||
out << "Vector: [";
|
||||
for (int i = 0; i < N; i++) {
|
||||
out << var.value[i];
|
||||
if (i < N - 1) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << "]";
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#include "LocalPoolVector.tpp"
|
||||
template <typename T, size_t N>
|
||||
using vec_t = PoolVector<T, N>;
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
using lp_vec_t = LocalPoolVector<T, vectorSize>;
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ */
|
||||
} // namespace datapool
|
||||
Whitespace-only changes.
File renamed without changes.
@@ -1,15 +1,19 @@
|
||||
#include "fsfw/datapool/PoolDataSetBase.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "fsfw/datapool/ReadCommitIFAttorney.h"
|
||||
#include "fsfw/globalfunctions/bitutility.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
|
||||
const size_t maxFillCount)
|
||||
: registeredVariables(registeredVariablesArray), maxFillCount(maxFillCount) {}
|
||||
const size_t maxFillCount, bool serializeWithValidityBlob)
|
||||
: serializeWithValidityBlob(serializeWithValidityBlob),
|
||||
registeredVariables(registeredVariablesArray),
|
||||
maxFillCount(maxFillCount) {}
|
||||
|
||||
PoolDataSetBase::~PoolDataSetBase() {}
|
||||
PoolDataSetBase::~PoolDataSetBase() = default;
|
||||
|
||||
ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF* variable) {
|
||||
if (registeredVariables == nullptr) {
|
||||
@@ -82,19 +86,19 @@ uint16_t PoolDataSetBase::getFillCount() const { return fillCount; }
|
||||
ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
if (registeredVariables[count] == nullptr) {
|
||||
/* Configuration error. */
|
||||
// Configuration error.
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
/* These checks are often performed by the respective variable implementation too, but I guess
|
||||
a double check does not hurt. */
|
||||
// These checks are often performed by the respective variable implementation too, but I guess
|
||||
// a double check does not hurt.
|
||||
if (registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_WRITE and
|
||||
registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER) {
|
||||
if (protectEveryReadCommitCall) {
|
||||
result =
|
||||
registeredVariables[count]->read(timeoutTypeForSingleVars, mutexTimeoutForSingleVars);
|
||||
} else {
|
||||
/* The readWithoutLock function is protected, so we use the attorney here */
|
||||
// The readWithoutLock function is protected, so we use the attorney here
|
||||
result = ReadCommitIFAttorney::readWithoutLock(registeredVariables[count]);
|
||||
}
|
||||
|
||||
@@ -172,6 +176,9 @@ ReturnValue_t PoolDataSetBase::unlockDataPool() { return returnvalue::OK; }
|
||||
|
||||
ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
if (this->serializeWithValidityBlob) {
|
||||
return doSerializeWithValidityBlob(buffer, size, maxSize, streamEndianness);
|
||||
}
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
|
||||
@@ -182,6 +189,51 @@ ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, const s
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::doSerializeWithValidityBlob(
|
||||
uint8_t** buffer, size_t* size, const size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
const uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount) / 8.0);
|
||||
uint8_t* validityPtr = nullptr;
|
||||
#if defined(_MSC_VER) || defined(__clang__)
|
||||
// Use a std::vector here because MSVC will (rightly) not create a fixed size array
|
||||
// with a non constant size specifier. The Apple compiler (LLVM) will not accept
|
||||
// the initialization of a variable sized array
|
||||
std::vector<uint8_t> validityMask(validityMaskSize, 0);
|
||||
validityPtr = validityMask.data();
|
||||
#else
|
||||
uint8_t validityMask[validityMaskSize] = {};
|
||||
validityPtr = validityMask;
|
||||
#endif
|
||||
uint8_t validBufferIndex = 0;
|
||||
uint8_t validBufferIndexBit = 0;
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
if (registeredVariables[count]->isValid()) {
|
||||
// Set bit at correct position
|
||||
bitutil::set(validityPtr + validBufferIndex, validBufferIndexBit);
|
||||
}
|
||||
if (validBufferIndexBit == 7) {
|
||||
validBufferIndex++;
|
||||
validBufferIndexBit = 0;
|
||||
} else {
|
||||
validBufferIndexBit++;
|
||||
}
|
||||
|
||||
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (*size + validityMaskSize > maxSize) {
|
||||
return SerializeIF::BUFFER_TOO_SHORT;
|
||||
}
|
||||
// copy validity buffer to end
|
||||
std::memcpy(*buffer, validityPtr, validityMaskSize);
|
||||
*size += validityMaskSize;
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t PoolDataSetBase::deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
@@ -199,6 +251,9 @@ size_t PoolDataSetBase::getSerializedSize() const {
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
size += registeredVariables[count]->getSerializedSize();
|
||||
}
|
||||
if (serializeWithValidityBlob) {
|
||||
size += std::ceil(static_cast<float>(fillCount) / 8.0);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -215,3 +270,9 @@ void PoolDataSetBase::setReadCommitProtectionBehaviour(bool protectEveryReadComm
|
||||
this->timeoutTypeForSingleVars = timeoutType;
|
||||
this->mutexTimeoutForSingleVars = mutexTimeout;
|
||||
}
|
||||
|
||||
void PoolDataSetBase::setChildrenValidity(bool valid) {
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
registeredVariables[count]->setValid(valid);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
#ifndef FSFW_DATAPOOL_POOLDATASETBASE_H_
|
||||
#define FSFW_DATAPOOL_POOLDATASETBASE_H_
|
||||
#pragma once
|
||||
|
||||
#include "PoolDataSetIF.h"
|
||||
#include "PoolVariableIF.h"
|
||||
#include "fsfw/ipc/MutexIF.h"
|
||||
#include "fsfw/serialize/SerializeIF.h"
|
||||
|
||||
namespace datapool {
|
||||
class SharedSetBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The DataSetBase class manages a set of locally checked out variables.
|
||||
* @details
|
||||
@@ -30,13 +33,16 @@
|
||||
* @ingroup data_pool
|
||||
*/
|
||||
class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
|
||||
friend class datapool::SharedSetBase;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Creates an empty dataset. Use registerVariable or
|
||||
* supply a pointer to this dataset to PoolVariable
|
||||
* initializations to register pool variables.
|
||||
*/
|
||||
PoolDataSetBase(PoolVariableIF** registeredVariablesArray, size_t maxFillCount);
|
||||
PoolDataSetBase(PoolVariableIF** registeredVariablesArray, size_t maxFillCount,
|
||||
bool serializeWithValidityBlob = true);
|
||||
|
||||
/* Forbidden for now */
|
||||
PoolDataSetBase(const PoolDataSetBase& otherSet) = delete;
|
||||
@@ -99,25 +105,26 @@ class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
|
||||
* thread-safety. Default implementation is empty
|
||||
* @return Always returns -@c returnvalue::OK
|
||||
*/
|
||||
virtual ReturnValue_t lockDataPool(
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20) override;
|
||||
ReturnValue_t lockDataPool(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20) override;
|
||||
/**
|
||||
* Provides the means to unlock the underlying data structure to ensure
|
||||
* thread-safety. Default implementation is empty
|
||||
* @return Always returns -@c returnvalue::OK
|
||||
*/
|
||||
virtual ReturnValue_t unlockDataPool() override;
|
||||
ReturnValue_t unlockDataPool() override;
|
||||
|
||||
virtual uint16_t getFillCount() const override;
|
||||
[[nodiscard]] uint16_t getFillCount() const override;
|
||||
|
||||
/* SerializeIF implementations */
|
||||
virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const override;
|
||||
virtual size_t getSerializedSize() const override;
|
||||
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) override;
|
||||
// SerializeIF implementations
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, const size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const override;
|
||||
[[nodiscard]] size_t getSerializedSize() const override;
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) override;
|
||||
|
||||
ReturnValue_t doSerializeWithValidityBlob(uint8_t** buffer, size_t* size, const size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const;
|
||||
/**
|
||||
* Can be used to individually protect every read and commit call.
|
||||
* @param protectEveryReadCommit
|
||||
@@ -127,6 +134,13 @@ class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
|
||||
bool protectEveryReadCommit, MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t mutexTimeout = 20);
|
||||
|
||||
/**
|
||||
* Set the validity of all children
|
||||
*/
|
||||
void setChildrenValidity(bool valid);
|
||||
|
||||
bool serializeWithValidityBlob = false;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief The fill_count attribute ensures that the variables
|
||||
@@ -168,6 +182,4 @@ class PoolDataSetBase : public PoolDataSetIF, public SerializeIF {
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, uint32_t timeoutMs = 20);
|
||||
ReturnValue_t handleUnreadDatasetCommit(
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, uint32_t timeoutMs = 20);
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOL_POOLDATASETBASE_H_ */
|
||||
};
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
class PoolDataSetIF : virtual public DataSetIF, virtual public ReadCommitIF {
|
||||
public:
|
||||
virtual ~PoolDataSetIF() {};
|
||||
~PoolDataSetIF() override = default;
|
||||
|
||||
/**
|
||||
* @brief Most underlying data structures will have a pool like structure
|
||||
@@ -18,17 +18,13 @@ class PoolDataSetIF : virtual public DataSetIF, virtual public ReadCommitIF {
|
||||
* thread-safety
|
||||
* @return Lock operation result
|
||||
*/
|
||||
virtual ReturnValue_t lockDataPool(
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20) = 0;
|
||||
virtual ReturnValue_t lockDataPool(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) = 0;
|
||||
|
||||
/**
|
||||
* @brief Unlock call corresponding to the lock call.
|
||||
* @return Unlock operation result
|
||||
*/
|
||||
virtual ReturnValue_t unlockDataPool() = 0;
|
||||
|
||||
virtual bool isValid() const = 0;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOL_POOLDATASETIF_H_ */
|
||||
@@ -104,16 +104,18 @@ class PoolEntry : public PoolEntryIF {
|
||||
* @brief This operation returns a the address pointer casted to void*.
|
||||
*/
|
||||
void* getRawData();
|
||||
|
||||
/**
|
||||
* @brief This method allows to set the valid information
|
||||
* of the pool entry.
|
||||
*/
|
||||
void setValid(bool isValid);
|
||||
void setValid(bool isValid) override;
|
||||
/**
|
||||
* @brief This method allows to get the valid information
|
||||
* of the pool entry.
|
||||
*/
|
||||
bool getValid();
|
||||
bool getValid() override;
|
||||
|
||||
/**
|
||||
* @brief This is a debug method that prints all values and the valid
|
||||
* information to the screen. It prints all array entries in a row.
|
||||
|
||||
@@ -39,14 +39,7 @@ class PoolEntryIF {
|
||||
* @brief This operation returns a the address pointer casted to void*.
|
||||
*/
|
||||
virtual void* getRawData() = 0;
|
||||
/**
|
||||
* @brief This method allows to set the valid information of the pool entry.
|
||||
*/
|
||||
virtual void setValid(bool isValid) = 0;
|
||||
/**
|
||||
* @brief This method allows to set the valid information of the pool entry.
|
||||
*/
|
||||
virtual bool getValid() = 0;
|
||||
|
||||
/**
|
||||
* @brief This is a debug method that prints all values and the valid
|
||||
* information to the screen. It prints all array entries in a row.
|
||||
@@ -58,6 +51,15 @@ class PoolEntryIF {
|
||||
* Returns the type of the entry.
|
||||
*/
|
||||
virtual Type getType() = 0;
|
||||
|
||||
/**
|
||||
* @brief This method allows to set the valid information of the pool entry.
|
||||
*/
|
||||
virtual void setValid(bool isValid) = 0;
|
||||
/**
|
||||
* @brief This method allows to set the valid information of the pool entry.
|
||||
*/
|
||||
virtual bool getValid() = 0;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOL_POOLENTRYIF_H_ */
|
||||
@@ -20,9 +20,9 @@ class PoolReadGuard {
|
||||
if (readResult != returnvalue::OK) {
|
||||
#if FSFW_VERBOSE_LEVEL == 1
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "PoolReadHelper: Read failed!" << std::endl;
|
||||
sif::error << "PoolReadGuard: Read failed!" << std::endl;
|
||||
#else
|
||||
sif::printError("PoolReadHelper: Read failed!\n");
|
||||
sif::printError("PoolReadGuard: Read failed!\n");
|
||||
#endif /* FSFW_PRINT_VERBOSITY_LEVEL == 1 */
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/datapool/internal/SharedPoolAttorney.h>
|
||||
|
||||
#include "FSFWConfig.h"
|
||||
#include "LocalPoolObjectBase.h"
|
||||
#include "fsfw/datapool/DataSetIF.h"
|
||||
#include "fsfw/datapool/PoolVariableIF.h"
|
||||
#include "fsfw/serialize/SerializeAdapter.h"
|
||||
|
||||
namespace datapool {
|
||||
|
||||
/**
|
||||
* @brief Local Pool Variable class which is used to access the local pools.
|
||||
* @details
|
||||
* This class is not stored in the map. Instead, it is used to access
|
||||
* the pool entries by using a pointer to the map storing the pool
|
||||
* entries. It can also be used to organize these pool entries into data sets.
|
||||
*
|
||||
* @tparam T The template parameter sets the type of the variable. Currently,
|
||||
* all plain data types are supported, but in principle any type is possible.
|
||||
* @ingroup data_pool
|
||||
*/
|
||||
template <typename T>
|
||||
class PoolVariable : public PoolObjectBase {
|
||||
public:
|
||||
//! Default ctor is forbidden.
|
||||
PoolVariable() = delete;
|
||||
|
||||
/**
|
||||
* This constructor is used by the data creators to have pool variable
|
||||
* instances which can also be stored in datasets.
|
||||
*
|
||||
* It does not fetch the current value from the data pool, which
|
||||
* has to be done by calling the read() operation.
|
||||
* Datasets can be used to access multiple local pool entries in an
|
||||
* efficient way. A pointer to a dataset can be passed to register
|
||||
* the pool variable in that dataset directly.
|
||||
* @param poolId ID of the local pool entry.
|
||||
* @param hkOwner Pointer of the owner. This will generally be the calling
|
||||
* class itself which passes "this".
|
||||
* @param dataSet The data set in which the variable shall register itself.
|
||||
* If nullptr, the variable is not registered.
|
||||
* @param setReadWriteMode Specify the read-write mode of the pool variable.
|
||||
*/
|
||||
PoolVariable(SharedPool& sharedPool, dp::id_t poolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
|
||||
|
||||
/**
|
||||
* This constructor is used by data users like controllers to have
|
||||
* access to the local pool variables of data creators by supplying
|
||||
* the respective creator object ID.
|
||||
*
|
||||
* It does not fetch the current value from the data pool, which
|
||||
* has to be done by calling the read() operation.
|
||||
* Datasets can be used to access multiple local pool entries in an
|
||||
* efficient way. A pointer to a dataset can be passed to register
|
||||
* the pool variable in that dataset directly.
|
||||
* @param poolId ID of the local pool entry.
|
||||
* @param hkOwner object ID of the pool owner.
|
||||
* @param dataSet The data set in which the variable shall register itself.
|
||||
* If nullptr, the variable is not registered.
|
||||
* @param setReadWriteMode Specify the read-write mode of the pool variable.
|
||||
*
|
||||
*/
|
||||
PoolVariable(object_id_t poolOwner, dp::id_t poolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
|
||||
/**
|
||||
* Variation which takes the global unique identifier of a pool variable.
|
||||
* @param globalPoolId
|
||||
* @param dataSet
|
||||
* @param setReadWriteMode
|
||||
*/
|
||||
PoolVariable(g_id_t globalPoolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
|
||||
|
||||
virtual ~PoolVariable() {};
|
||||
|
||||
T get() const;
|
||||
|
||||
/**
|
||||
* @brief This is the local copy of the data pool entry.
|
||||
* @details The user can work on this attribute
|
||||
* just like he would on a simple local variable.
|
||||
*/
|
||||
T value = 0;
|
||||
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const override;
|
||||
size_t getSerializedSize() const override;
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) override;
|
||||
|
||||
/**
|
||||
* @brief This is a call to read the array's values
|
||||
* from the global data pool.
|
||||
* @details
|
||||
* When executed, this operation tries to fetch the pool entry with matching
|
||||
* data pool id from the data pool and copies all array values and the valid
|
||||
* information to its local attributes.
|
||||
* In case of a failure (wrong type, size or pool id not found), the
|
||||
* variable is set to zero and invalid.
|
||||
* The read call is protected with a lock.
|
||||
* It is recommended to use DataSets to read and commit multiple variables
|
||||
* at once to avoid the overhead of unnecessary lock und unlock operations.
|
||||
*
|
||||
*/
|
||||
ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20) override;
|
||||
/**
|
||||
* @brief The commit call copies the array values back to the data pool.
|
||||
* @details
|
||||
* It checks type and size, as well as if the variable is writable. If so,
|
||||
* the value is copied and the local valid flag is written back as well.
|
||||
* The read call is protected with a lock.
|
||||
* It is recommended to use DataSets to read and commit multiple variables
|
||||
* at once to avoid the overhead of unnecessary lock und unlock operations.
|
||||
*/
|
||||
ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20) override;
|
||||
|
||||
PoolVariable<T>& operator=(const T& newValue);
|
||||
PoolVariable<T>& operator=(const PoolVariable<T>& newPoolVariable);
|
||||
|
||||
//! Explicit type conversion operator. Allows casting the class to
|
||||
//! its template type to perform operations on value.
|
||||
explicit operator T() const;
|
||||
|
||||
bool operator==(const PoolVariable<T>& other) const;
|
||||
bool operator==(const T& other) const;
|
||||
|
||||
bool operator!=(const PoolVariable<T>& other) const;
|
||||
bool operator!=(const T& other) const;
|
||||
|
||||
bool operator<(const PoolVariable<T>& other) const;
|
||||
bool operator<(const T& other) const;
|
||||
|
||||
bool operator>(const PoolVariable<T>& other) const;
|
||||
bool operator>(const T& other) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Like #read, but without a lock protection of the global pool.
|
||||
* @details
|
||||
* The operation does NOT provide any mutual exclusive protection by itself.
|
||||
* This can be used if the lock is handled externally to avoid the overhead
|
||||
* of consecutive lock und unlock operations.
|
||||
* Declared protected to discourage free public usage.
|
||||
*/
|
||||
ReturnValue_t readWithoutLock() override;
|
||||
/**
|
||||
* @brief Like #commit, but without a lock protection of the global pool.
|
||||
* @details
|
||||
* The operation does NOT provide any mutual exclusive protection by itself.
|
||||
* This can be used if the lock is handled externally to avoid the overhead
|
||||
* of consecutive lock und unlock operations.
|
||||
* Declared protected to discourage free public usage.
|
||||
*/
|
||||
ReturnValue_t commitWithoutLock() override;
|
||||
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
// std::ostream is the type for object std::cout
|
||||
template <typename U>
|
||||
friend std::ostream& operator<<(std::ostream& out, const LocalPoolVariable<U>& var);
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
PoolVariable<T>::PoolVariable(SharedPool& sharedPool, dp::id_t poolId, DataSetIF* dataSet,
|
||||
pool_rwm_t setReadWriteMode)
|
||||
: PoolObjectBase(sharedPool, poolId, dataSet, setReadWriteMode) {}
|
||||
|
||||
template <typename T>
|
||||
PoolVariable<T>::PoolVariable(object_id_t poolOwner, dp::id_t poolId, DataSetIF* dataSet,
|
||||
pool_rwm_t setReadWriteMode)
|
||||
: PoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
|
||||
|
||||
template <typename T>
|
||||
PoolVariable<T>::PoolVariable(g_id_t globalPoolId, DataSetIF* dataSet, pool_rwm_t setReadWriteMode)
|
||||
: PoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, dataSet, setReadWriteMode) {}
|
||||
|
||||
template <typename T>
|
||||
ReturnValue_t PoolVariable<T>::read(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
||||
if (sharedPool == nullptr) {
|
||||
return readWithoutLock();
|
||||
}
|
||||
MutexIF* mutex = sharedPool->getPoolMutex();
|
||||
ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
result = readWithoutLock();
|
||||
mutex->unlockMutex();
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ReturnValue_t PoolVariable<T>::readWithoutLock() {
|
||||
if (sharedPool == nullptr) {
|
||||
return PoolVariableIF::INVALID_SHARED_POOL;
|
||||
}
|
||||
if (readWriteMode == pool_rwm_t::VAR_WRITE) {
|
||||
return PoolVariableIF::INVALID_READ_WRITE_MODE;
|
||||
}
|
||||
|
||||
PoolEntry<T>* poolEntry = nullptr;
|
||||
if (ReturnValue_t result = sharedPool->fetchPoolEntry(localPoolId, &poolEntry);
|
||||
result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
this->value = *(poolEntry->getDataPtr());
|
||||
this->valid = poolEntry->getValid();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ReturnValue_t PoolVariable<T>::commit(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
||||
if (sharedPool == nullptr) {
|
||||
return commitWithoutLock();
|
||||
}
|
||||
MutexIF* mutex = sharedPool->getPoolMutex();
|
||||
ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
result = commitWithoutLock();
|
||||
mutex->unlockMutex();
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ReturnValue_t PoolVariable<T>::commitWithoutLock() {
|
||||
if (readWriteMode == pool_rwm_t::VAR_READ) {
|
||||
return PoolVariableIF::INVALID_READ_WRITE_MODE;
|
||||
}
|
||||
|
||||
PoolEntry<T>* poolEntry = nullptr;
|
||||
ReturnValue_t result = sharedPool->fetchPoolEntry(localPoolId, &poolEntry);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
*(poolEntry->getDataPtr()) = this->value;
|
||||
poolEntry->setValid(this->valid);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ReturnValue_t PoolVariable<T>::serialize(uint8_t** buffer, size_t* size, const size_t max_size,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
return SerializeAdapter::serialize(&value, buffer, size, max_size, streamEndianness);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t PoolVariable<T>::getSerializedSize() const {
|
||||
return SerializeAdapter::getSerializedSize(&value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ReturnValue_t PoolVariable<T>::deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) {
|
||||
return SerializeAdapter::deSerialize(&value, buffer, size, streamEndianness);
|
||||
}
|
||||
template <typename T>
|
||||
T PoolVariable<T>::get() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
template <typename T>
|
||||
inline std::ostream& operator<<(std::ostream& out, const PoolVariable<T>& var) {
|
||||
out << var.value;
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
PoolVariable<T>::operator T() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
PoolVariable<T>& PoolVariable<T>::operator=(const T& newValue) {
|
||||
value = newValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
PoolVariable<T>& PoolVariable<T>::operator=(const PoolVariable<T>& newPoolVariable) {
|
||||
value = newPoolVariable.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool PoolVariable<T>::operator==(const PoolVariable<T>& other) const {
|
||||
return this->value == other.value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool PoolVariable<T>::operator==(const T& other) const {
|
||||
return this->value == other;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool PoolVariable<T>::operator!=(const PoolVariable& other) const {
|
||||
return not(*this == other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool PoolVariable<T>::operator!=(const T& other) const {
|
||||
return not(*this == other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool PoolVariable<T>::operator<(const PoolVariable<T>& other) const {
|
||||
return this->value < other.value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool PoolVariable<T>::operator<(const T& other) const {
|
||||
return this->value < other;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool PoolVariable<T>::operator>(const PoolVariable<T>& other) const {
|
||||
return not(*this < other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool PoolVariable<T>::operator>(const T& other) const {
|
||||
return not(*this < other);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
using var_t = PoolVariable<T>;
|
||||
|
||||
using bool_t = PoolVariable<uint8_t>;
|
||||
using u8_t = PoolVariable<uint8_t>;
|
||||
using u16_t = PoolVariable<uint16_t>;
|
||||
using u32_t = PoolVariable<uint32_t>;
|
||||
using u64_t = PoolVariable<uint64_t>;
|
||||
using i8_t = PoolVariable<int8_t>;
|
||||
using i16_t = PoolVariable<int16_t>;
|
||||
using i32_t = PoolVariable<int32_t>;
|
||||
using i64_t = PoolVariable<int64_t>;
|
||||
using f32_t = PoolVariable<float>;
|
||||
using f64_t = PoolVariable<double>;
|
||||
|
||||
} // namespace datapool
|
||||
@@ -0,0 +1 @@
|
||||
#pragma once
|
||||
@@ -23,6 +23,7 @@ class PoolVariableIF : public SerializeIF, public ReadCommitIF {
|
||||
static constexpr uint8_t INTERFACE_ID = CLASS_ID::POOL_VARIABLE_IF;
|
||||
static constexpr ReturnValue_t INVALID_READ_WRITE_MODE = MAKE_RETURN_CODE(0xA0);
|
||||
static constexpr ReturnValue_t INVALID_POOL_ENTRY = MAKE_RETURN_CODE(0xA1);
|
||||
static constexpr ReturnValue_t INVALID_SHARED_POOL = MAKE_RETURN_CODE(0xA2);
|
||||
|
||||
static constexpr bool VALID = 1;
|
||||
static constexpr bool INVALID = 0;
|
||||
@@ -46,15 +47,9 @@ class PoolVariableIF : public SerializeIF, public ReadCommitIF {
|
||||
* @brief This operation shall return the data pool id of the variable.
|
||||
*/
|
||||
virtual uint32_t getDataPoolId() const = 0;
|
||||
/**
|
||||
* @brief With this call, the valid information of the
|
||||
* variable is returned.
|
||||
*/
|
||||
|
||||
virtual bool isValid() const = 0;
|
||||
/**
|
||||
* @brief With this call, the valid information of the variable is set.
|
||||
*/
|
||||
virtual void setValid(bool validity) = 0;
|
||||
virtual void setValid(bool valid) = 0;
|
||||
};
|
||||
|
||||
using pool_rwm_t = PoolVariableIF::ReadWriteMode_t;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "SharedPool.h"
|
||||
|
||||
#include "FSFWConfig.h"
|
||||
#include "fsfw/ipc/MutexFactory.h"
|
||||
#include "fsfw/serviceinterface.h"
|
||||
|
||||
using namespace dp;
|
||||
SharedPool::SharedPool(object_id_t ownerId) : ownerId(ownerId) {
|
||||
mutex = MutexFactory::instance()->createMutex();
|
||||
if (mutex == nullptr) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "localpool::Manager: Mutex creation failed" << std::endl;
|
||||
#else
|
||||
sif::printError("localpool::Manager: Mutex creation failed");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
SharedPool::~SharedPool() { MutexFactory::instance()->deleteMutex(mutex); }
|
||||
|
||||
object_id_t SharedPool::getOwnerId() const { return ownerId; }
|
||||
|
||||
void SharedPool::addPoolEntry(id_t poolId, PoolEntryIF* entry) {
|
||||
localPoolMap.emplace(poolId, entry);
|
||||
}
|
||||
|
||||
MutexIF* SharedPool::getPoolMutex() { return mutex; }
|
||||
|
||||
ReturnValue_t SharedPool::printPoolEntry(id_t localPoolId) {
|
||||
auto poolIter = localPoolMap.find(localPoolId);
|
||||
if (poolIter == localPoolMap.end()) {
|
||||
return POOL_ENTRY_NOT_FOUND;
|
||||
}
|
||||
poolIter->second->print();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include "fsfw/datapool/PoolEntry.h"
|
||||
#include "fsfw/datapool/definitions.h"
|
||||
#include "fsfw/ipc/MutexIF.h"
|
||||
|
||||
namespace datapool {
|
||||
|
||||
class SharedPool {
|
||||
public:
|
||||
explicit SharedPool(object_id_t ownerId);
|
||||
~SharedPool();
|
||||
|
||||
[[nodiscard]] object_id_t getOwnerId() const;
|
||||
|
||||
/**
|
||||
* Read a variable by supplying its local pool ID and assign the pool
|
||||
* entry to the supplied PoolEntry pointer. The type of the pool entry
|
||||
* is deduced automatically. This call is not thread-safe!
|
||||
* For now, only classes designated by the LocalDpManagerAttorney may use this function.
|
||||
* @tparam T Type of the pool entry
|
||||
* @param localPoolId Pool ID of the variable to read
|
||||
* @param poolVar [out] Corresponding pool entry will be assigned to the
|
||||
* supplied pointer.
|
||||
* @return
|
||||
*/
|
||||
template <class T>
|
||||
ReturnValue_t fetchPoolEntry(id_t localPoolId, PoolEntry<T>** poolEntry);
|
||||
ReturnValue_t printPoolEntry(id_t localPoolId);
|
||||
void addPoolEntry(id_t poolId, PoolEntryIF* entry);
|
||||
MutexIF* getPoolMutex();
|
||||
|
||||
private:
|
||||
object_id_t ownerId;
|
||||
|
||||
// Core data structure for the actual pool data
|
||||
DataPool localPoolMap{};
|
||||
// Every housekeeping data manager has a mutex to protect access
|
||||
// to it's data pool.
|
||||
MutexIF* mutex = nullptr;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline ReturnValue_t datapool::SharedPool::fetchPoolEntry(id_t localPoolId,
|
||||
PoolEntry<T>** poolEntry) {
|
||||
if (poolEntry == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
auto poolIter = localPoolMap.find(localPoolId);
|
||||
if (poolIter == localPoolMap.end()) {
|
||||
return POOL_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
*poolEntry = dynamic_cast<PoolEntry<T>*>(poolIter->second);
|
||||
if (*poolEntry == nullptr) {
|
||||
return POOL_ENTRY_TYPE_CONFLICT;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
} // namespace datapool
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "SharedSet.h"
|
||||
|
||||
using namespace dp;
|
||||
|
||||
SharedSet::SharedSet(dp::SharedPool& sharedPool, uint32_t setId, const size_t maxNumberOfVariables,
|
||||
bool serializeWithValidityBlob)
|
||||
: SharedSetBase(sharedPool, setId, nullptr, maxNumberOfVariables, serializeWithValidityBlob),
|
||||
poolVarList(maxNumberOfVariables) {
|
||||
this->setContainer(poolVarList.data());
|
||||
}
|
||||
|
||||
SharedSet::SharedSet(dp::structure_id_t sid, const size_t maxNumberOfVariables,
|
||||
bool serializeWithValidityBlob)
|
||||
: SharedSetBase(sid, nullptr, maxNumberOfVariables, serializeWithValidityBlob),
|
||||
poolVarList(maxNumberOfVariables) {
|
||||
this->setContainer(poolVarList.data());
|
||||
}
|
||||
|
||||
SharedSet::~SharedSet() = default;
|
||||
@@ -1,10 +1,11 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALDATASET_H_
|
||||
#define FSFW_DATAPOOLLOCAL_LOCALDATASET_H_
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "LocalPoolDataSetBase.h"
|
||||
#include "SharedPool.h"
|
||||
#include "SharedSetBase.h"
|
||||
|
||||
namespace datapool {
|
||||
/**
|
||||
* @brief This dataset type can be used to group related pool variables if the number of
|
||||
* variables should not be fixed.
|
||||
@@ -18,20 +19,21 @@
|
||||
* @tparam capacity Capacity of the static dataset, which is usually known
|
||||
* beforehand.
|
||||
*/
|
||||
class LocalDataSet : public LocalPoolDataSetBase {
|
||||
class SharedSet : public SharedSetBase {
|
||||
public:
|
||||
LocalDataSet(HasLocalDataPoolIF* hkOwner, uint32_t setId, const size_t maxSize);
|
||||
SharedSet(SharedPool& sharedPool, uint32_t setId, size_t maxSize,
|
||||
bool serializeWithValidityBlob = true);
|
||||
|
||||
LocalDataSet(sid_t sid, const size_t maxSize);
|
||||
SharedSet(sid_t sid, size_t maxSize, bool serializeWithValidityBlob = true);
|
||||
|
||||
virtual ~LocalDataSet();
|
||||
~SharedSet() override;
|
||||
|
||||
//! Copying forbidden for now.
|
||||
LocalDataSet(const LocalDataSet&) = delete;
|
||||
LocalDataSet& operator=(const LocalDataSet&) = delete;
|
||||
SharedSet(const SharedSet&) = delete;
|
||||
SharedSet& operator=(const SharedSet&) = delete;
|
||||
|
||||
private:
|
||||
std::vector<PoolVariableIF*> poolVarList;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_LOCALDATASET_H_ */
|
||||
} // namespace datapool
|
||||
@@ -0,0 +1,169 @@
|
||||
#include "SharedSetBase.h"
|
||||
|
||||
#include <fsfw/housekeeping/GeneratesPeriodicHkIF.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "fsfw/datapool.h"
|
||||
#include "fsfw/globalfunctions/bitutility.h"
|
||||
#include "fsfw/objectmanager/ObjectManager.h"
|
||||
#include "fsfw/serialize/SerializeAdapter.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
using namespace datapool;
|
||||
|
||||
SharedSetBase::SharedSetBase(SharedPool &sharedPool, uint32_t setId,
|
||||
PoolVariableIF **registeredVariablesArray,
|
||||
const size_t maxNumberOfVariables, bool serializeWithValidityBlob)
|
||||
: base(registeredVariablesArray, maxNumberOfVariables, serializeWithValidityBlob),
|
||||
sharedPool(&sharedPool) {
|
||||
mutexIfSingleDataCreator = sharedPool.getPoolMutex();
|
||||
|
||||
this->sid.objectId = sharedPool.getOwnerId();
|
||||
this->sid.ownerSetId = setId;
|
||||
}
|
||||
|
||||
SharedSetBase::SharedSetBase(structure_id_t sid, PoolVariableIF **registeredVariablesArray,
|
||||
const size_t maxNumberOfVariables, bool serializeWithValidityBlob)
|
||||
: base(registeredVariablesArray, maxNumberOfVariables, serializeWithValidityBlob) {
|
||||
auto *hkOwner = ObjectManager::instance()->get<hk::GeneratesPeriodicHkIF>(sid.objectId);
|
||||
if (hkOwner != nullptr) {
|
||||
sharedPool = hkOwner->getOptionalSharedPool();
|
||||
if (sharedPool != nullptr) {
|
||||
mutexIfSingleDataCreator = sharedPool->getPoolMutex();
|
||||
}
|
||||
}
|
||||
|
||||
this->sid = sid;
|
||||
}
|
||||
|
||||
SharedSetBase::SharedSetBase(PoolVariableIF **registeredVariablesArray,
|
||||
const size_t maxNumberOfVariables, bool protectEveryReadCommitCall,
|
||||
bool serializeWithValidityBlob)
|
||||
: base(registeredVariablesArray, maxNumberOfVariables, serializeWithValidityBlob) {
|
||||
base.setReadCommitProtectionBehaviour(protectEveryReadCommitCall);
|
||||
}
|
||||
|
||||
SharedSetBase::~SharedSetBase() {
|
||||
// In case set was read but not comitted, we commit all variables with an invalid state
|
||||
if (base.state == PoolDataSetBase::States::STATE_SET_WAS_READ) {
|
||||
for (uint16_t count = 0; count < base.getFillCount(); count++) {
|
||||
if (base.registeredVariables[count] != nullptr) {
|
||||
base.registeredVariables[count]->commit(MutexIF::TimeoutType::WAITING, 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t SharedSetBase::lockDataPool(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
||||
if (mutexIfSingleDataCreator != nullptr) {
|
||||
return mutexIfSingleDataCreator->lockMutex(timeoutType, timeoutMs);
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t SharedSetBase::unlockDataPool() {
|
||||
if (mutexIfSingleDataCreator != nullptr) {
|
||||
return mutexIfSingleDataCreator->unlockMutex();
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t SharedSetBase::serializeLocalPoolIds(uint8_t **buffer, size_t *size, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
/* Serialize fill count as uint8_t */
|
||||
auto fillCount = this->getFillCount();
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
id_t currentPoolId = base.registeredVariables[count]->getDataPoolId();
|
||||
auto result =
|
||||
SerializeAdapter::serialize(¤tPoolId, buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "LocalPoolDataSetBase::serializeLocalPoolIds: " << "Serialization error!"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning(
|
||||
"LocalPoolDataSetBase::serializeLocalPoolIds: "
|
||||
"Serialization error!\n\r");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
size_t SharedSetBase::getLocalPoolIdsSerializedSize() const {
|
||||
return base.getFillCount() * sizeof(id_t);
|
||||
}
|
||||
|
||||
size_t SharedSetBase::getSerializedSize() const { return base.getSerializedSize(); }
|
||||
|
||||
ReturnValue_t SharedSetBase::deSerialize(const uint8_t **buffer, size_t *size,
|
||||
SerializeIF::Endianness streamEndianness) {
|
||||
return base.deSerialize(buffer, size, streamEndianness);
|
||||
}
|
||||
|
||||
ReturnValue_t SharedSetBase::serialize(uint8_t **buffer, size_t *size, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
return base.serialize(buffer, size, maxSize, streamEndianness);
|
||||
}
|
||||
|
||||
[[nodiscard]] ReturnValue_t SharedSetBase::serialize(
|
||||
uint8_t *buffer, size_t &serLen, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
return SerializeIF::serialize(buffer, serLen, maxSize, streamEndianness);
|
||||
}
|
||||
|
||||
void SharedSetBase::setReportingEnabled(bool reportingEnabled) {
|
||||
this->reportingEnabled = reportingEnabled;
|
||||
}
|
||||
|
||||
bool SharedSetBase::getReportingEnabled() const { return reportingEnabled; }
|
||||
|
||||
structure_id_t SharedSetBase::getStructureId() const { return sid; }
|
||||
|
||||
void SharedSetBase::setChildrenValidity(bool valid) { return base.setChildrenValidity(valid); }
|
||||
|
||||
object_id_t SharedSetBase::getCreatorObjectId() {
|
||||
if (sharedPool != nullptr) {
|
||||
return sharedPool->getOwnerId();
|
||||
}
|
||||
return objects::NO_OBJECT;
|
||||
}
|
||||
|
||||
void SharedSetBase::setAllVariablesReadOnly() {
|
||||
for (size_t idx = 0; idx < this->getFillCount(); idx++) {
|
||||
base.registeredVariables[idx]->setReadWriteMode(pool_rwm_t::VAR_READ);
|
||||
}
|
||||
}
|
||||
|
||||
void SharedSetBase::printSet() { return; }
|
||||
|
||||
ReturnValue_t SharedSetBase::read(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
||||
lockDataPool(timeoutType, timeoutMs);
|
||||
ReturnValue_t result = base.read(timeoutType, timeoutMs);
|
||||
unlockDataPool();
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t SharedSetBase::commit(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
||||
lockDataPool(timeoutType, timeoutMs);
|
||||
ReturnValue_t result = base.commit(timeoutType, timeoutMs);
|
||||
unlockDataPool();
|
||||
return result;
|
||||
}
|
||||
uint16_t SharedSetBase::getFillCount() const { return base.getFillCount(); }
|
||||
|
||||
ReturnValue_t SharedSetBase::registerVariable(PoolVariableIF *variable) {
|
||||
return base.registerVariable(variable);
|
||||
}
|
||||
|
||||
void SharedSetBase::setContainer(PoolVariableIF **variablesContainer) {
|
||||
return base.setContainer(variablesContainer);
|
||||
}
|
||||
PoolVariableIF **SharedSetBase::getContainer() const { return base.getContainer(); }
|
||||
|
||||
void SharedSetBase::updateValidityBlobSerialization(bool enable) {
|
||||
base.serializeWithValidityBlob = enable;
|
||||
}
|
||||
bool SharedSetBase::getValidityBlobSerialization() const { return base.serializeWithValidityBlob; }
|
||||
@@ -1,17 +1,16 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_
|
||||
#define FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "MarkChangedIF.h"
|
||||
#include "SharedPool.h"
|
||||
#include "definitions.h"
|
||||
#include "fsfw/datapool/DataSetIF.h"
|
||||
#include "fsfw/datapool/PoolDataSetBase.h"
|
||||
#include "localPoolDefinitions.h"
|
||||
|
||||
class LocalDataPoolManager;
|
||||
class HasLocalDataPoolIF;
|
||||
class PeriodicHkGenerationHelper;
|
||||
class PeriodicHkGenerationIF;
|
||||
class PeriodicHousekeepingHelper;
|
||||
|
||||
namespace datapool {
|
||||
|
||||
/**
|
||||
* @brief The LocalDataSet class manages a set of locally checked out
|
||||
* variables for local data pools
|
||||
@@ -40,10 +39,7 @@ class PeriodicHousekeepingHelper;
|
||||
*
|
||||
* @ingroup data_pool
|
||||
*/
|
||||
class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
|
||||
friend class LocalPoolDataSetAttorney;
|
||||
friend class PeriodicHousekeepingHelper;
|
||||
|
||||
class SharedSetBase : public SerializeIF, public PoolDataSetIF {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor for the creator of local pool data.
|
||||
@@ -51,9 +47,8 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
|
||||
* This constructor also initializes the components required for
|
||||
* periodic handling.
|
||||
*/
|
||||
LocalPoolDataSetBase(HasLocalDataPoolIF* hkOwner, uint32_t setId,
|
||||
PoolVariableIF** registeredVariablesArray, const size_t maxNumberOfVariables,
|
||||
bool periodicHandling = true);
|
||||
SharedSetBase(SharedPool& sharedPool, uint32_t setId, PoolVariableIF** registeredVariablesArray,
|
||||
size_t maxNumberOfVariables, bool serializeWithValidityBlob = true);
|
||||
|
||||
/**
|
||||
* @brief Constructor for users of the local pool data, which need
|
||||
@@ -66,8 +61,8 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
|
||||
* @param registeredVariablesArray
|
||||
* @param maxNumberOfVariables
|
||||
*/
|
||||
LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray,
|
||||
const size_t maxNumberOfVariables);
|
||||
SharedSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray, size_t maxNumberOfVariables,
|
||||
bool serializeWithValidityBlob = true);
|
||||
|
||||
/**
|
||||
* @brief Simple constructor, if the dataset is not the owner by
|
||||
@@ -87,8 +82,8 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
|
||||
* multiple creators, this flag can be set to protect all read and
|
||||
* commit calls separately.
|
||||
*/
|
||||
LocalPoolDataSetBase(PoolVariableIF** registeredVariablesArray, const size_t maxNumberOfVariables,
|
||||
bool protectEveryReadCommitCall = true);
|
||||
SharedSetBase(PoolVariableIF** registeredVariablesArray, size_t maxNumberOfVariables,
|
||||
bool serializeWithValidityBlob, bool protectEveryReadCommitCall = true);
|
||||
|
||||
/**
|
||||
* @brief The destructor automatically manages writing the valid
|
||||
@@ -98,13 +93,13 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
|
||||
* the destructor parses all variables that are still registered to the set.
|
||||
* For each, the valid flag in the data pool is set to "invalid".
|
||||
*/
|
||||
~LocalPoolDataSetBase();
|
||||
~SharedSetBase() override;
|
||||
|
||||
/* The copy constructor and assingment constructor are forbidden for now.
|
||||
The use-cases are limited and the first step would be to implement them properly for the
|
||||
base class */
|
||||
LocalPoolDataSetBase(const LocalPoolDataSetBase& otherSet) = delete;
|
||||
const LocalPoolDataSetBase& operator=(const LocalPoolDataSetBase& otherSet) = delete;
|
||||
SharedSetBase(const SharedSetBase& otherSet) = delete;
|
||||
const SharedSetBase& operator=(const SharedSetBase& otherSet) = delete;
|
||||
|
||||
/**
|
||||
* Helper functions used to set all currently contained variables to read-only.
|
||||
@@ -112,56 +107,27 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
|
||||
* by data consumers to prevent accidentally changing pool data.
|
||||
*/
|
||||
void setAllVariablesReadOnly();
|
||||
void setValidityBufferGeneration(bool withValidityBuffer);
|
||||
|
||||
sid_t getSid() const;
|
||||
[[nodiscard]] virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
Endianness streamEndianness) const override;
|
||||
|
||||
/** SerializeIF overrides */
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const override;
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) override;
|
||||
size_t getSerializedSize() const override;
|
||||
[[nodiscard]] ReturnValue_t serialize(uint8_t* buffer, size_t& serLen, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const override;
|
||||
|
||||
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
Endianness streamEndianness) override;
|
||||
|
||||
[[nodiscard]] dp::sid_t getStructureId() const;
|
||||
|
||||
[[nodiscard]] size_t getSerializedSize() const override;
|
||||
|
||||
/**
|
||||
* Special version of the serilization function which appends a
|
||||
* validity buffer at the end. Each bit of this validity buffer
|
||||
* denotes whether the container data set entries are valid from left
|
||||
* to right, MSB first. (length = ceil(N/8), N = number of pool variables)
|
||||
* @param buffer
|
||||
* @param size
|
||||
* @param maxSize
|
||||
* @param bigEndian
|
||||
* @param withValidityBuffer
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t serializeWithValidityBuffer(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const;
|
||||
ReturnValue_t deSerializeWithValidityBuffer(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness);
|
||||
ReturnValue_t serializeLocalPoolIds(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness,
|
||||
bool serializeFillCount = true) const;
|
||||
uint8_t getLocalPoolIdsSerializedSize(bool serializeFillCount = true) const;
|
||||
|
||||
/**
|
||||
* Set the dataset valid or invalid. These calls are mutex protected.
|
||||
* @param setEntriesRecursively
|
||||
* If this is true, all contained datasets will also be set recursively.
|
||||
*/
|
||||
void setValidity(bool valid, bool setEntriesRecursively);
|
||||
bool isValid() const override;
|
||||
|
||||
/**
|
||||
* These calls are mutex protected.
|
||||
* @param changed
|
||||
*/
|
||||
void setChanged(bool changed) override;
|
||||
bool hasChanged() const override;
|
||||
SerializeIF::Endianness streamEndianness) const;
|
||||
[[nodiscard]] size_t getLocalPoolIdsSerializedSize() const;
|
||||
|
||||
object_id_t getCreatorObjectId();
|
||||
|
||||
bool getReportingEnabled() const;
|
||||
[[nodiscard]] bool getReportingEnabled() const;
|
||||
void setReportingEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
@@ -170,14 +136,35 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
|
||||
* returns 0.0
|
||||
* @return
|
||||
*/
|
||||
float getCollectionInterval() const;
|
||||
[[nodiscard]] float getCollectionInterval() const;
|
||||
|
||||
/**
|
||||
* @brief Can be overwritten by a specific implementation of a dataset to print the set.
|
||||
*/
|
||||
virtual void printSet();
|
||||
|
||||
ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::BLOCKING,
|
||||
dur_millis_t timeoutMs = 0) override;
|
||||
ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::BLOCKING,
|
||||
dur_millis_t timeoutMs = 0) override;
|
||||
uint16_t getFillCount() const override;
|
||||
|
||||
ReturnValue_t registerVariable(PoolVariableIF* variable) override;
|
||||
|
||||
void setContainer(PoolVariableIF** variablesContainer);
|
||||
PoolVariableIF** getContainer() const;
|
||||
|
||||
/**
|
||||
* Set the validity of all children
|
||||
*/
|
||||
void setChildrenValidity(bool valid);
|
||||
|
||||
void updateValidityBlobSerialization(bool enable);
|
||||
bool getValidityBlobSerialization() const;
|
||||
|
||||
protected:
|
||||
PoolDataSetBase base;
|
||||
|
||||
sid_t sid;
|
||||
//! This mutex is used if the data is created by one object only.
|
||||
MutexIF* mutexIfSingleDataCreator = nullptr;
|
||||
@@ -189,27 +176,6 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
|
||||
|
||||
void initializePeriodicHelper(float collectionInterval, dur_millis_t minimumPeriodicInterval);
|
||||
|
||||
/**
|
||||
* If the valid state of a dataset is always relevant to the whole
|
||||
* data set we can use this flag.
|
||||
*/
|
||||
bool valid = false;
|
||||
|
||||
/**
|
||||
* Can be used to mark the dataset as changed, which is used
|
||||
* by the LocalDataPoolManager to send out update messages.
|
||||
*/
|
||||
bool changed = false;
|
||||
|
||||
/**
|
||||
* Specify whether the validity buffer is serialized too when serializing
|
||||
* or deserializing the packet. Each bit of the validity buffer will
|
||||
* contain the validity state of the pool variables from left to right.
|
||||
* The size of validity buffer thus will be ceil(N / 8) with N = number of
|
||||
* pool variables.
|
||||
*/
|
||||
bool withValidityBuffer = true;
|
||||
|
||||
/**
|
||||
* @brief This is a small helper function to facilitate locking
|
||||
* the global data pool.
|
||||
@@ -226,8 +192,7 @@ class LocalPoolDataSetBase : public PoolDataSetBase, public MarkChangedIF {
|
||||
*/
|
||||
ReturnValue_t unlockDataPool() override;
|
||||
|
||||
PeriodicHousekeepingHelper* periodicHelper = nullptr;
|
||||
LocalDataPoolManager* poolManager = nullptr;
|
||||
dp::SharedPool* sharedPool = nullptr;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_ */
|
||||
} // namespace datapool
|
||||
@@ -1,13 +1,10 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_
|
||||
#define FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "../objectmanager/SystemObjectIF.h"
|
||||
#include "LocalPoolDataSetBase.h"
|
||||
#include "LocalPoolVariable.h"
|
||||
#include "LocalPoolVector.h"
|
||||
#include "SharedSetBase.h"
|
||||
|
||||
namespace datapool {
|
||||
/**
|
||||
* @brief This dataset type can be used to group related pool variables if the number of
|
||||
* variables is fixed.
|
||||
@@ -18,30 +15,31 @@
|
||||
*
|
||||
* It is recommended to read the documentation of the LocalPoolDataSetBase
|
||||
* class for more information on how this class works and how to use it.
|
||||
* @tparam capacity Capacity of the static dataset, which is usually known
|
||||
* @tparam NUM_VARIABLES Capacity of the static dataset, which is usually known
|
||||
* beforehand.
|
||||
*/
|
||||
template <uint8_t NUM_VARIABLES>
|
||||
class StaticLocalDataSet : public LocalPoolDataSetBase {
|
||||
template <size_t NUM_VARIABLES>
|
||||
class StaticSharedSet : public datapool::SharedSetBase {
|
||||
public:
|
||||
/**
|
||||
* Constructor used by data owner and creator like device handlers.
|
||||
* This constructor also initialized the components required for
|
||||
* periodic handling.
|
||||
* @param hkOwner
|
||||
* @param sharedPool Shared pool this dataset will read from or write to.
|
||||
* @param setId
|
||||
*/
|
||||
StaticLocalDataSet(HasLocalDataPoolIF* hkOwner, uint32_t setId)
|
||||
: LocalPoolDataSetBase(hkOwner, setId, nullptr, NUM_VARIABLES) {
|
||||
StaticSharedSet(SharedPool& sharedPool, const uint32_t setId,
|
||||
bool serializeWithValidityBlob = true)
|
||||
: SharedSetBase(sharedPool, setId, nullptr, NUM_VARIABLES, serializeWithValidityBlob) {
|
||||
this->setContainer(poolVarList.data());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor used by data users like controllers.
|
||||
* @param hkOwner
|
||||
* @param setId
|
||||
* @param sid
|
||||
*/
|
||||
StaticLocalDataSet(sid_t sid) : LocalPoolDataSetBase(sid, nullptr, NUM_VARIABLES) {
|
||||
explicit StaticSharedSet(const structure_id_t sid, bool serializeWithValidityBlob = true)
|
||||
: SharedSetBase(sid, nullptr, NUM_VARIABLES, serializeWithValidityBlob) {
|
||||
this->setContainer(poolVarList.data());
|
||||
}
|
||||
|
||||
@@ -49,4 +47,4 @@ class StaticLocalDataSet : public LocalPoolDataSetBase {
|
||||
std::array<PoolVariableIF*, NUM_VARIABLES> poolVarList = {};
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_ */
|
||||
} // namespace datapool
|
||||
@@ -1,5 +1,4 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_
|
||||
#define FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
@@ -8,44 +7,44 @@
|
||||
#include "../objectmanager/SystemObjectIF.h"
|
||||
#include "../objectmanager/frameworkObjects.h"
|
||||
|
||||
namespace datapool {
|
||||
|
||||
/**
|
||||
* @brief Type definition for local pool entries.
|
||||
*/
|
||||
using lp_id_t = uint32_t;
|
||||
using id_t = uint32_t;
|
||||
|
||||
namespace localpool {
|
||||
static constexpr uint32_t INVALID_LPID = -1;
|
||||
|
||||
static constexpr uint8_t INTERFACE_ID = CLASS_ID::LOCAL_POOL_OWNER_IF;
|
||||
static constexpr uint8_t INTERFACE_ID = CLASS_ID::DATAPOOL_IF;
|
||||
|
||||
static constexpr ReturnValue_t POOL_ENTRY_NOT_FOUND = MAKE_RETURN_CODE(0x00);
|
||||
static constexpr ReturnValue_t POOL_ENTRY_TYPE_CONFLICT = MAKE_RETURN_CODE(0x01);
|
||||
|
||||
/** This is the core data structure of the local data pools. Users should insert all desired
|
||||
pool variables, using the std::map interface. */
|
||||
using DataPool = std::map<lp_id_t, PoolEntryIF*>;
|
||||
using DataPoolMapIter = DataPool::iterator;
|
||||
|
||||
} // namespace localpool
|
||||
using DataPool = std::map<id_t, PoolEntryIF*>;
|
||||
|
||||
/**
|
||||
* Used as a unique identifier for data sets. Consists of 4 byte object ID and 4 byte set ID.
|
||||
*/
|
||||
union sid_t {
|
||||
union structure_id_t {
|
||||
static constexpr size_t SIZE = sizeof(object_id_t) + sizeof(uint32_t);
|
||||
|
||||
static constexpr uint64_t INVALID_SID = -1;
|
||||
static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT;
|
||||
static constexpr uint32_t INVALID_SET_ID = -1;
|
||||
|
||||
sid_t() : raw(INVALID_SID) {}
|
||||
constexpr structure_id_t() : raw(INVALID_SID) {}
|
||||
|
||||
sid_t(object_id_t objectId, uint32_t setId) : objectId(objectId), ownerSetId(setId) {}
|
||||
constexpr structure_id_t(object_id_t objectId, uint32_t setId)
|
||||
: objectId(objectId), ownerSetId(setId) {}
|
||||
|
||||
struct {
|
||||
object_id_t objectId;
|
||||
/**
|
||||
* A generic 32 bit ID to identify unique HK packets for a single
|
||||
* object. For example, the DeviceCommandId_t is used for
|
||||
* DeviceHandlers
|
||||
* A generic 32 bit ID to identify unique HK set packets for a single
|
||||
* object.
|
||||
*/
|
||||
uint32_t ownerSetId;
|
||||
};
|
||||
@@ -54,39 +53,41 @@ union sid_t {
|
||||
*/
|
||||
uint64_t raw;
|
||||
|
||||
bool notSet() const { return raw == INVALID_SID; }
|
||||
[[nodiscard]] bool notSet() const { return raw == INVALID_SID; }
|
||||
|
||||
bool operator==(const sid_t& other) const { return raw == other.raw; }
|
||||
bool operator==(const structure_id_t& other) const { return raw == other.raw; }
|
||||
|
||||
bool operator!=(const sid_t& other) const { return not(raw == other.raw); }
|
||||
bool operator!=(const structure_id_t& other) const { return not(raw == other.raw); }
|
||||
};
|
||||
|
||||
using sid_t = structure_id_t;
|
||||
|
||||
/**
|
||||
* Used as a global unique identifier for local pool variables. Consists of 4 byte object ID
|
||||
* and 4 byte local pool ID.
|
||||
*/
|
||||
union gp_id_t {
|
||||
union g_id_t {
|
||||
static constexpr uint64_t INVALID_GPID = -1;
|
||||
static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT;
|
||||
static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID;
|
||||
|
||||
gp_id_t() : raw(INVALID_GPID) {}
|
||||
g_id_t() : raw(INVALID_GPID) {}
|
||||
|
||||
gp_id_t(object_id_t objectId, lp_id_t localPoolId)
|
||||
: objectId(objectId), localPoolId(localPoolId) {}
|
||||
g_id_t(object_id_t objectId, id_t localPoolId) : objectId(objectId), localPoolId(localPoolId) {}
|
||||
|
||||
struct {
|
||||
object_id_t objectId;
|
||||
lp_id_t localPoolId;
|
||||
id_t localPoolId;
|
||||
};
|
||||
|
||||
uint64_t raw;
|
||||
|
||||
bool notSet() const { return raw == INVALID_GPID; }
|
||||
|
||||
bool operator==(const gp_id_t& other) const { return raw == other.raw; }
|
||||
bool operator==(const g_id_t& other) const { return raw == other.raw; }
|
||||
|
||||
bool operator!=(const gp_id_t& other) const { return not(raw == other.raw); }
|
||||
bool operator!=(const g_id_t& other) const { return not(raw == other.raw); }
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_ */
|
||||
} // namespace datapool
|
||||
|
||||
namespace dp = datapool;
|
||||
@@ -0,0 +1 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE)
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "fsfw/datapool/SharedSetBase.h"
|
||||
|
||||
class LocalPoolDataSetAttorney {
|
||||
static void setReportingEnabled(SharedDatasetBase& set, bool enabled) {
|
||||
set.setReportingEnabled(enabled);
|
||||
}
|
||||
|
||||
static bool getReportingEnabled(SharedDatasetBase& set) { return set.getReportingEnabled(); }
|
||||
};
|
||||
+7
-7
@@ -1,8 +1,8 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_
|
||||
#define FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_
|
||||
#pragma once
|
||||
|
||||
#include "../LocalDataPoolManager.h"
|
||||
#include "fsfw/datapool/SharedPool.h"
|
||||
|
||||
namespace datapool {
|
||||
/**
|
||||
* @brief This is a helper class implements the Attorney-Client idiom for access to
|
||||
* LocalDataPoolManager internals
|
||||
@@ -13,15 +13,15 @@
|
||||
* an explicit subset of the pool manager private/protected functions.
|
||||
* See: https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Friendship_and_the_Attorney-Client
|
||||
*/
|
||||
class LocalDpManagerAttorney {
|
||||
class SharedPoolAttorney {
|
||||
private:
|
||||
template <typename T>
|
||||
static ReturnValue_t fetchPoolEntry(LocalDataPoolManager& manager, lp_id_t localPoolId,
|
||||
static ReturnValue_t fetchPoolEntry(SharedPool& manager, dp::id_t localPoolId,
|
||||
PoolEntry<T>** poolEntry) {
|
||||
return manager.fetchPoolEntry(localPoolId, poolEntry);
|
||||
}
|
||||
|
||||
static MutexIF* getMutexHandle(LocalDataPoolManager& manager) { return manager.getMutexHandle(); }
|
||||
static MutexIF* getMutexHandle(SharedPool& manager) { return manager.getPoolMutex(); }
|
||||
|
||||
template <typename T>
|
||||
friend class LocalPoolVariable;
|
||||
@@ -29,4 +29,4 @@ class LocalDpManagerAttorney {
|
||||
friend class LocalPoolVector;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_ */
|
||||
} // namespace datapool
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||
#define FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||
|
||||
/* Collected related headers */
|
||||
#include "fsfw/datapoollocal/LocalDataSet.h"
|
||||
#include "fsfw/datapoollocal/LocalPoolVariable.h"
|
||||
#include "fsfw/datapoollocal/LocalPoolVector.h"
|
||||
#include "fsfw/datapoollocal/SharedLocalDataSet.h"
|
||||
#include "fsfw/datapoollocal/StaticLocalDataSet.h"
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ */
|
||||
@@ -1,26 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_
|
||||
#define FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_
|
||||
|
||||
class LocalDataPoolManager;
|
||||
class MutexIF;
|
||||
|
||||
/**
|
||||
* @brief Accessor class which can be used by classes which like to use the pool manager.
|
||||
*/
|
||||
class AccessPoolManagerIF {
|
||||
public:
|
||||
virtual ~AccessPoolManagerIF() {};
|
||||
|
||||
virtual MutexIF* getLocalPoolMutex() = 0;
|
||||
|
||||
/**
|
||||
* Can be used to get a handle to the local data pool manager.
|
||||
* This function is protected because it should only be used by the
|
||||
* class imlementing the interface.
|
||||
*/
|
||||
virtual LocalDataPoolManager* getPoolManagerHandle() = 0;
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_ */
|
||||
@@ -1,6 +0,0 @@
|
||||
target_sources(
|
||||
${LIB_FSFW_NAME}
|
||||
PRIVATE LocalDataPoolManager.cpp LocalDataSet.cpp LocalPoolDataSetBase.cpp
|
||||
LocalPoolObjectBase.cpp SharedLocalDataSet.cpp)
|
||||
|
||||
add_subdirectory(internal)
|
||||
@@ -1,181 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_
|
||||
#define FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "LocalDataPoolManager.h"
|
||||
#include "fsfw/datapool/PoolEntryIF.h"
|
||||
#include "fsfw/housekeeping/HousekeepingMessage.h"
|
||||
#include "fsfw/ipc/MessageQueueSenderIF.h"
|
||||
#include "fsfw/serviceinterface.h"
|
||||
#include "localPoolDefinitions.h"
|
||||
|
||||
class AccessPoolManagerIF;
|
||||
class ProvidesDataPoolSubscriptionIF;
|
||||
class LocalPoolDataSetBase;
|
||||
class LocalPoolObjectBase;
|
||||
|
||||
/**
|
||||
* @brief This interface is implemented by classes which posses a local data pool (not
|
||||
* the managing class). It defines the relationship between the local data pool owner and the
|
||||
* LocalDataPoolManager.
|
||||
* @details
|
||||
* Any class implementing this interface shall also have a LocalDataPoolManager member class which
|
||||
* contains the actual pool data structure and exposes the public interface for it.
|
||||
*
|
||||
* The local data pool can be accessed using helper classes by using the
|
||||
* LocalPoolVariable, LocalPoolVector or LocalDataSet classes. Every local pool variable can
|
||||
* be uniquely identified by a global pool ID (gp_id_t) and every dataset tied
|
||||
* to a pool manager can be uniqely identified by a global structure ID (sid_t).
|
||||
*
|
||||
* All software objects which want to use the local pool of another object shall also use this
|
||||
* interface, for example to get a handle to the subscription interface. The interface
|
||||
* can be retrieved using the object manager, provided the target object is a SystemObject.
|
||||
* For example, the following line of code can be used to retrieve the interface
|
||||
*
|
||||
* HasLocalDataPoolIF* poolIF = ObjectManager::instance()->
|
||||
* get<HasLocalDataPoolIF>(objects::SOME_OBJECT);
|
||||
* if(poolIF != nullptr) {
|
||||
* doSomething()
|
||||
* }
|
||||
*/
|
||||
class HasLocalDataPoolIF {
|
||||
friend class HasLocalDpIFManagerAttorney;
|
||||
friend class HasLocalDpIFUserAttorney;
|
||||
|
||||
public:
|
||||
virtual ~HasLocalDataPoolIF() {};
|
||||
|
||||
static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID;
|
||||
|
||||
virtual object_id_t getObjectId() const = 0;
|
||||
|
||||
/** Command queue for housekeeping messages. */
|
||||
virtual MessageQueueId_t getCommandQueue() const = 0;
|
||||
|
||||
/**
|
||||
* Is used by pool owner to initialize the pool map once
|
||||
* The manager instance shall also be passed to this function.
|
||||
* It can be used to subscribe for periodic packets for for updates.
|
||||
*/
|
||||
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) = 0;
|
||||
|
||||
/**
|
||||
* Returns the minimum sampling frequency in milliseconds, which will
|
||||
* usually be the period the pool owner performs its periodic operation.
|
||||
* @return
|
||||
*/
|
||||
virtual dur_millis_t getPeriodicOperationFrequency() const = 0;
|
||||
|
||||
/**
|
||||
* @brief This function will be called by the manager if an update
|
||||
* notification is received.
|
||||
* @details HasLocalDataPoolIF
|
||||
* Can be overriden by the child class to handle changed datasets.
|
||||
* @param sid SID of the updated set
|
||||
* @param storeId If a snapshot was requested, data will be located inside
|
||||
* the IPC store with this store ID.
|
||||
* @param clearMessage If this is set to true, the pool manager will take care of
|
||||
* clearing the store automatically
|
||||
*/
|
||||
virtual void handleChangedDataset(sid_t sid, store_address_t storeId = store_address_t::invalid(),
|
||||
bool* clearMessage = nullptr) {
|
||||
if (clearMessage != nullptr) {
|
||||
*clearMessage = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function will be called by the manager if an update
|
||||
* notification is received.
|
||||
* @details
|
||||
* Can be overriden by the child class to handle changed pool variables.
|
||||
* @param gpid GPID of the updated variable.
|
||||
* @param storeId If a snapshot was requested, data will be located inside
|
||||
* the IPC store with this store ID.
|
||||
* @param clearMessage Relevant for snapshots. If the boolean this points to is set to true,
|
||||
* the pool manager will take care of clearing the store automatically
|
||||
* after the callback.
|
||||
*/
|
||||
virtual void handleChangedPoolVariable(gp_id_t gpid,
|
||||
store_address_t storeId = store_address_t::invalid(),
|
||||
bool* clearMessage = nullptr) {
|
||||
if (clearMessage != nullptr) {
|
||||
*clearMessage = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* These function can be implemented by pool owner, if they are required
|
||||
* and used by the housekeeping message interface.
|
||||
* */
|
||||
virtual ReturnValue_t addDataSet(sid_t sid) { return returnvalue::FAILED; };
|
||||
virtual ReturnValue_t removeDataSet(sid_t sid) { return returnvalue::FAILED; };
|
||||
virtual ReturnValue_t changeCollectionInterval(sid_t sid, float newIntervalSeconds) {
|
||||
return returnvalue::FAILED;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function can be used by data pool consumers to retrieve a handle
|
||||
* which allows subscriptions to dataset and variable updates in form of messages.
|
||||
* The consumers can then read the most recent variable value by calling read with
|
||||
* an own pool variable or set instance or using the deserialized snapshot data.
|
||||
* Returns the HK manager casted to the required interface by default.
|
||||
* @return
|
||||
*/
|
||||
virtual ProvidesDataPoolSubscriptionIF* getSubscriptionInterface() {
|
||||
return getHkManagerHandle();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Every class implementing this interface should have a local data pool manager. This
|
||||
* function will return a reference to the manager.
|
||||
* @return
|
||||
*/
|
||||
virtual LocalDataPoolManager* getHkManagerHandle() = 0;
|
||||
|
||||
/**
|
||||
* Accessor handle required for internal handling. Not intended for users and therefore
|
||||
* declared protected. Users should instead use pool variables, sets or the subscription
|
||||
* interface to access pool entries. Returns the HK manager casted to a different interface
|
||||
* by default.
|
||||
* @return
|
||||
*/
|
||||
virtual AccessPoolManagerIF* getAccessorHandle() { return getHkManagerHandle(); }
|
||||
|
||||
/**
|
||||
* This function is used by the pool manager to get a valid dataset
|
||||
* from a SID. This function is protected to prevent users from
|
||||
* using raw data set pointers which could not be thread-safe. Users
|
||||
* should use the #ProvidesDataPoolSubscriptionIF.
|
||||
* @param sid Corresponding structure ID
|
||||
* @return
|
||||
*/
|
||||
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) = 0;
|
||||
|
||||
/**
|
||||
* Similar to the function above, but used to get a local pool variable
|
||||
* handle. This is only needed for update notifications, so it is not
|
||||
* defined as abstract. This function is protected to prevent users from
|
||||
* using raw pool variable pointers which could not be thread-safe.
|
||||
* Users should use the #ProvidesDataPoolSubscriptionIF.
|
||||
* @param localPoolId
|
||||
* @return
|
||||
*/
|
||||
virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden. "
|
||||
"Returning nullptr!"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning(
|
||||
"HasLocalDataPoolIF::getPoolObjectHandle: "
|
||||
"Not overriden. Returning nullptr!\n");
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_ */
|
||||
@@ -1,817 +0,0 @@
|
||||
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "fsfw/datapoollocal.h"
|
||||
#include "fsfw/housekeeping/AcceptsHkPacketsIF.h"
|
||||
#include "fsfw/housekeeping/HousekeepingSetPacket.h"
|
||||
#include "fsfw/housekeeping/HousekeepingSnapshot.h"
|
||||
#include "fsfw/ipc/MutexFactory.h"
|
||||
#include "fsfw/ipc/MutexGuard.h"
|
||||
#include "fsfw/ipc/QueueFactory.h"
|
||||
#include "fsfw/objectmanager/ObjectManager.h"
|
||||
#include "fsfw/timemanager/CCSDSTime.h"
|
||||
#include "internal/HasLocalDpIFManagerAttorney.h"
|
||||
#include "internal/LocalPoolDataSetAttorney.h"
|
||||
|
||||
// TODO: Get rid of this. This should be a constructor argument, not something hardcoded in any way
|
||||
object_id_t LocalDataPoolManager::defaultHkDestination = objects::PUS_SERVICE_3_HOUSEKEEPING;
|
||||
|
||||
LocalDataPoolManager::LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQueueIF* queueToUse,
|
||||
bool appendValidityBuffer)
|
||||
: appendValidityBuffer(appendValidityBuffer) {
|
||||
if (owner == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "LocalDataPoolManager", returnvalue::FAILED,
|
||||
"Invalid supplied owner");
|
||||
return;
|
||||
}
|
||||
this->owner = owner;
|
||||
mutex = MutexFactory::instance()->createMutex();
|
||||
if (mutex == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_ERROR, "LocalDataPoolManager", returnvalue::FAILED,
|
||||
"Could not create mutex");
|
||||
}
|
||||
|
||||
hkQueue = queueToUse;
|
||||
}
|
||||
|
||||
LocalDataPoolManager::~LocalDataPoolManager() {
|
||||
if (mutex != nullptr) {
|
||||
MutexFactory::instance()->deleteMutex(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) {
|
||||
if (queueToUse == nullptr) {
|
||||
/* Error, all destinations invalid */
|
||||
printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize", QUEUE_OR_DESTINATION_INVALID);
|
||||
}
|
||||
hkQueue = queueToUse;
|
||||
|
||||
ipcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::IPC_STORE);
|
||||
if (ipcStore == nullptr) {
|
||||
/* Error, all destinations invalid */
|
||||
printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize", returnvalue::FAILED,
|
||||
"Could not set IPC store.");
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
if (defaultHkDestination != objects::NO_OBJECT) {
|
||||
auto* hkPacketReceiver =
|
||||
ObjectManager::instance()->get<AcceptsHkPacketsIF>(defaultHkDestination);
|
||||
if (hkPacketReceiver != nullptr) {
|
||||
hkDestinationId = hkPacketReceiver->getHkQueue();
|
||||
} else {
|
||||
printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize", QUEUE_OR_DESTINATION_INVALID);
|
||||
return QUEUE_OR_DESTINATION_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::initializeAfterTaskCreation() {
|
||||
return initializeHousekeepingPoolEntriesOnce();
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::initializeHousekeepingPoolEntriesOnce() {
|
||||
if (not mapInitialized) {
|
||||
ReturnValue_t result = owner->initializeLocalDataPool(localPoolMap, *this);
|
||||
if (result == returnvalue::OK) {
|
||||
mapInitialized = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "initializeHousekeepingPoolEntriesOnce",
|
||||
returnvalue::FAILED, "The map should only be initialized once");
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::performHkOperation() {
|
||||
ReturnValue_t status = returnvalue::OK;
|
||||
for (auto& receiver : hkReceivers) {
|
||||
switch (receiver.reportingType) {
|
||||
case (ReportingType::PERIODIC): {
|
||||
if (receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
|
||||
/* Periodic packets shall only be generated from datasets */
|
||||
continue;
|
||||
}
|
||||
performPeriodicHkGeneration(receiver);
|
||||
break;
|
||||
}
|
||||
case (ReportingType::UPDATE_HK): {
|
||||
handleHkUpdate(receiver, status);
|
||||
break;
|
||||
}
|
||||
case (ReportingType::UPDATE_NOTIFICATION): {
|
||||
handleNotificationUpdate(receiver, status);
|
||||
break;
|
||||
}
|
||||
case (ReportingType::UPDATE_SNAPSHOT): {
|
||||
handleNotificationSnapshot(receiver, status);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// This should never happen.
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
}
|
||||
resetHkUpdateResetHelper();
|
||||
return status;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::handleHkUpdate(HkReceiver& receiver, ReturnValue_t& status) {
|
||||
if (receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
|
||||
/* Update packets shall only be generated from datasets. */
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
LocalPoolDataSetBase* dataSet =
|
||||
HasLocalDpIFManagerAttorney::getDataSetHandle(owner, receiver.dataId.sid);
|
||||
if (dataSet == nullptr) {
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
if (dataSet->hasChanged()) {
|
||||
/* Prepare and send update notification */
|
||||
ReturnValue_t result = generateHousekeepingPacket(receiver.dataId.sid, dataSet, true);
|
||||
if (result != returnvalue::OK) {
|
||||
status = result;
|
||||
}
|
||||
}
|
||||
handleChangeResetLogic(receiver.dataType, receiver.dataId, dataSet);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::handleNotificationUpdate(HkReceiver& receiver,
|
||||
ReturnValue_t& status) {
|
||||
MarkChangedIF* toReset = nullptr;
|
||||
if (receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
|
||||
LocalPoolObjectBase* poolObj =
|
||||
HasLocalDpIFManagerAttorney::getPoolObjectHandle(owner, receiver.dataId.localPoolId);
|
||||
if (poolObj == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleNotificationUpdate",
|
||||
POOLOBJECT_NOT_FOUND);
|
||||
return POOLOBJECT_NOT_FOUND;
|
||||
}
|
||||
if (poolObj->hasChanged()) {
|
||||
/* Prepare and send update notification. */
|
||||
CommandMessage notification;
|
||||
HousekeepingMessage::setUpdateNotificationVariableCommand(
|
||||
¬ification, gp_id_t(owner->getObjectId(), receiver.dataId.localPoolId));
|
||||
ReturnValue_t result = hkQueue->sendMessage(receiver.destinationQueue, ¬ification);
|
||||
if (result != returnvalue::OK) {
|
||||
status = result;
|
||||
}
|
||||
toReset = poolObj;
|
||||
}
|
||||
|
||||
} else {
|
||||
LocalPoolDataSetBase* dataSet =
|
||||
HasLocalDpIFManagerAttorney::getDataSetHandle(owner, receiver.dataId.sid);
|
||||
if (dataSet == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleNotificationUpdate",
|
||||
DATASET_NOT_FOUND);
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
if (dataSet->hasChanged()) {
|
||||
/* Prepare and send update notification */
|
||||
CommandMessage notification;
|
||||
HousekeepingMessage::setUpdateNotificationSetCommand(¬ification, receiver.dataId.sid);
|
||||
ReturnValue_t result = hkQueue->sendMessage(receiver.destinationQueue, ¬ification);
|
||||
if (result != returnvalue::OK) {
|
||||
status = result;
|
||||
}
|
||||
toReset = dataSet;
|
||||
}
|
||||
}
|
||||
if (toReset != nullptr) {
|
||||
handleChangeResetLogic(receiver.dataType, receiver.dataId, toReset);
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::handleNotificationSnapshot(HkReceiver& receiver,
|
||||
ReturnValue_t& status) {
|
||||
MarkChangedIF* toReset = nullptr;
|
||||
/* Check whether data has changed and send messages in case it has */
|
||||
if (receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
|
||||
LocalPoolObjectBase* poolObj =
|
||||
HasLocalDpIFManagerAttorney::getPoolObjectHandle(owner, receiver.dataId.localPoolId);
|
||||
if (poolObj == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleNotificationSnapshot",
|
||||
POOLOBJECT_NOT_FOUND);
|
||||
return POOLOBJECT_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (not poolObj->hasChanged()) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
/* Prepare and send update snapshot */
|
||||
timeval now{};
|
||||
Clock::getClock_timeval(&now);
|
||||
CCSDSTime::CDS_short cds{};
|
||||
CCSDSTime::convertToCcsds(&cds, &now);
|
||||
HousekeepingSnapshot updatePacket(
|
||||
reinterpret_cast<uint8_t*>(&cds), sizeof(cds),
|
||||
HasLocalDpIFManagerAttorney::getPoolObjectHandle(owner, receiver.dataId.localPoolId));
|
||||
|
||||
store_address_t storeId;
|
||||
ReturnValue_t result = addUpdateToStore(updatePacket, storeId);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
CommandMessage notification;
|
||||
HousekeepingMessage::setUpdateSnapshotVariableCommand(
|
||||
¬ification, gp_id_t(owner->getObjectId(), receiver.dataId.localPoolId), storeId);
|
||||
result = hkQueue->sendMessage(receiver.destinationQueue, ¬ification);
|
||||
if (result != returnvalue::OK) {
|
||||
status = result;
|
||||
}
|
||||
toReset = poolObj;
|
||||
} else {
|
||||
LocalPoolDataSetBase* dataSet =
|
||||
HasLocalDpIFManagerAttorney::getDataSetHandle(owner, receiver.dataId.sid);
|
||||
if (dataSet == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleNotificationSnapshot",
|
||||
DATASET_NOT_FOUND);
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (not dataSet->hasChanged()) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
/* Prepare and send update snapshot */
|
||||
timeval now{};
|
||||
Clock::getClock_timeval(&now);
|
||||
CCSDSTime::CDS_short cds{};
|
||||
CCSDSTime::convertToCcsds(&cds, &now);
|
||||
HousekeepingSnapshot updatePacket(
|
||||
reinterpret_cast<uint8_t*>(&cds), sizeof(cds),
|
||||
HasLocalDpIFManagerAttorney::getDataSetHandle(owner, receiver.dataId.sid));
|
||||
|
||||
store_address_t storeId;
|
||||
ReturnValue_t result = addUpdateToStore(updatePacket, storeId);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
CommandMessage notification;
|
||||
HousekeepingMessage::setUpdateSnapshotSetCommand(¬ification, receiver.dataId.sid, storeId);
|
||||
result = hkQueue->sendMessage(receiver.destinationQueue, ¬ification);
|
||||
if (result != returnvalue::OK) {
|
||||
status = result;
|
||||
}
|
||||
toReset = dataSet;
|
||||
}
|
||||
if (toReset != nullptr) {
|
||||
handleChangeResetLogic(receiver.dataType, receiver.dataId, toReset);
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::addUpdateToStore(HousekeepingSnapshot& updatePacket,
|
||||
store_address_t& storeId) {
|
||||
size_t updatePacketSize = updatePacket.getSerializedSize();
|
||||
uint8_t* storePtr = nullptr;
|
||||
ReturnValue_t result =
|
||||
ipcStore->getFreeElement(&storeId, updatePacket.getSerializedSize(), &storePtr);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
size_t serializedSize = 0;
|
||||
result = updatePacket.serialize(&storePtr, &serializedSize, updatePacketSize,
|
||||
SerializeIF::Endianness::MACHINE);
|
||||
return result;
|
||||
;
|
||||
}
|
||||
|
||||
void LocalDataPoolManager::handleChangeResetLogic(DataType type, DataId dataId,
|
||||
MarkChangedIF* toReset) {
|
||||
for (auto& changeInfo : hkUpdateResetList) {
|
||||
if (changeInfo.dataType != type) {
|
||||
continue;
|
||||
}
|
||||
if ((changeInfo.dataType == DataType::DATA_SET) and (changeInfo.dataId.sid != dataId.sid)) {
|
||||
continue;
|
||||
}
|
||||
if ((changeInfo.dataType == DataType::LOCAL_POOL_VARIABLE) and
|
||||
(changeInfo.dataId.localPoolId != dataId.localPoolId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Only one update recipient, we can reset changes status immediately */
|
||||
if (changeInfo.updateCounter <= 1) {
|
||||
toReset->setChanged(false);
|
||||
}
|
||||
/* All recipients have been notified, reset the changed flag */
|
||||
else if (changeInfo.currentUpdateCounter <= 1) {
|
||||
toReset->setChanged(false);
|
||||
changeInfo.currentUpdateCounter = 0;
|
||||
}
|
||||
/* Not all recipiens have been notified yet, decrement */
|
||||
else {
|
||||
changeInfo.currentUpdateCounter--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void LocalDataPoolManager::resetHkUpdateResetHelper() {
|
||||
for (auto& changeInfo : hkUpdateResetList) {
|
||||
changeInfo.currentUpdateCounter = changeInfo.updateCounter;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::subscribeForRegularPeriodicPacket(
|
||||
subdp::RegularHkPeriodicParams params) {
|
||||
return subscribeForPeriodicPacket(params);
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::subscribeForDiagPeriodicPacket(
|
||||
subdp::DiagnosticsHkPeriodicParams params) {
|
||||
return subscribeForPeriodicPacket(params);
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(subdp::ParamsBase& params) {
|
||||
struct HkReceiver hkReceiver;
|
||||
hkReceiver.dataId.sid = params.sid;
|
||||
hkReceiver.reportingType = ReportingType::PERIODIC;
|
||||
hkReceiver.dataType = DataType::DATA_SET;
|
||||
if (params.receiver == MessageQueueIF::NO_QUEUE) {
|
||||
hkReceiver.destinationQueue = hkDestinationId;
|
||||
} else {
|
||||
hkReceiver.destinationQueue = params.receiver;
|
||||
}
|
||||
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, params.sid);
|
||||
if (dataSet != nullptr) {
|
||||
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, params.enableReporting);
|
||||
LocalPoolDataSetAttorney::initializePeriodicHelper(*dataSet, params.collectionInterval,
|
||||
owner->getPeriodicOperationFrequency());
|
||||
}
|
||||
|
||||
hkReceivers.push_back(hkReceiver);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::subscribeForRegularUpdatePacket(
|
||||
subdp::RegularHkUpdateParams params) {
|
||||
return subscribeForUpdatePacket(params);
|
||||
}
|
||||
ReturnValue_t LocalDataPoolManager::subscribeForDiagUpdatePacket(
|
||||
subdp::DiagnosticsHkUpdateParams params) {
|
||||
return subscribeForUpdatePacket(params);
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::subscribeForUpdatePacket(subdp::ParamsBase& params) {
|
||||
struct HkReceiver hkReceiver;
|
||||
hkReceiver.dataId.sid = params.sid;
|
||||
hkReceiver.reportingType = ReportingType::UPDATE_HK;
|
||||
hkReceiver.dataType = DataType::DATA_SET;
|
||||
if (params.receiver == MessageQueueIF::NO_QUEUE) {
|
||||
hkReceiver.destinationQueue = hkDestinationId;
|
||||
} else {
|
||||
hkReceiver.destinationQueue = params.receiver;
|
||||
}
|
||||
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, params.sid);
|
||||
if (dataSet != nullptr) {
|
||||
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, true);
|
||||
}
|
||||
|
||||
hkReceivers.push_back(hkReceiver);
|
||||
|
||||
handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::subscribeForSetUpdateMessage(const uint32_t setId,
|
||||
object_id_t destinationObject,
|
||||
MessageQueueId_t targetQueueId,
|
||||
bool generateSnapshot) {
|
||||
struct HkReceiver hkReceiver;
|
||||
hkReceiver.dataType = DataType::DATA_SET;
|
||||
hkReceiver.dataId.sid = sid_t(owner->getObjectId(), setId);
|
||||
hkReceiver.destinationQueue = targetQueueId;
|
||||
hkReceiver.objectId = destinationObject;
|
||||
if (generateSnapshot) {
|
||||
hkReceiver.reportingType = ReportingType::UPDATE_SNAPSHOT;
|
||||
} else {
|
||||
hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION;
|
||||
}
|
||||
|
||||
hkReceivers.push_back(hkReceiver);
|
||||
|
||||
handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::subscribeForVariableUpdateMessage(
|
||||
const lp_id_t localPoolId, object_id_t destinationObject, MessageQueueId_t targetQueueId,
|
||||
bool generateSnapshot) {
|
||||
struct HkReceiver hkReceiver;
|
||||
hkReceiver.dataType = DataType::LOCAL_POOL_VARIABLE;
|
||||
hkReceiver.dataId.localPoolId = localPoolId;
|
||||
hkReceiver.destinationQueue = targetQueueId;
|
||||
hkReceiver.objectId = destinationObject;
|
||||
if (generateSnapshot) {
|
||||
hkReceiver.reportingType = ReportingType::UPDATE_SNAPSHOT;
|
||||
} else {
|
||||
hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION;
|
||||
}
|
||||
|
||||
hkReceivers.push_back(hkReceiver);
|
||||
|
||||
handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
void LocalDataPoolManager::handleHkUpdateResetListInsertion(DataType dataType, DataId dataId) {
|
||||
for (auto& updateResetStruct : hkUpdateResetList) {
|
||||
if (dataType == DataType::DATA_SET) {
|
||||
if (updateResetStruct.dataId.sid == dataId.sid) {
|
||||
updateResetStruct.updateCounter++;
|
||||
updateResetStruct.currentUpdateCounter++;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (updateResetStruct.dataId.localPoolId == dataId.localPoolId) {
|
||||
updateResetStruct.updateCounter++;
|
||||
updateResetStruct.currentUpdateCounter++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
HkUpdateResetHelper hkUpdateResetHelper;
|
||||
hkUpdateResetHelper.currentUpdateCounter = 1;
|
||||
hkUpdateResetHelper.updateCounter = 1;
|
||||
hkUpdateResetHelper.dataType = dataType;
|
||||
if (dataType == DataType::DATA_SET) {
|
||||
hkUpdateResetHelper.dataId.sid = dataId.sid;
|
||||
} else {
|
||||
hkUpdateResetHelper.dataId.localPoolId = dataId.localPoolId;
|
||||
}
|
||||
hkUpdateResetList.push_back(hkUpdateResetHelper);
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(CommandMessage* message) {
|
||||
Command_t command = message->getCommand();
|
||||
sid_t sid = HousekeepingMessage::getSid(message);
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
switch (command) {
|
||||
// Houskeeping interface handling.
|
||||
case (HousekeepingMessage::ENABLE_PERIODIC_HK_REPORT_GENERATION): {
|
||||
result = togglePeriodicGeneration(sid, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case (HousekeepingMessage::DISABLE_PERIODIC_HK_REPORT_GENERATION): {
|
||||
result = togglePeriodicGeneration(sid, false);
|
||||
break;
|
||||
}
|
||||
|
||||
case (HousekeepingMessage::REPORT_HK_REPORT_STRUCTURES): {
|
||||
result = generateSetStructurePacket(sid);
|
||||
if (result == returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (HousekeepingMessage::MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL): {
|
||||
float newCollIntvl = 0;
|
||||
HousekeepingMessage::getCollectionIntervalModificationCommand(message, &newCollIntvl);
|
||||
result = changeCollectionInterval(sid, newCollIntvl);
|
||||
break;
|
||||
}
|
||||
|
||||
case (HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT): {
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||
if (dataSet == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "handleHousekeepingMessage",
|
||||
DATASET_NOT_FOUND);
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
return generateHousekeepingPacket(HousekeepingMessage::getSid(message), dataSet, true);
|
||||
}
|
||||
|
||||
/* Notification handling */
|
||||
case (HousekeepingMessage::UPDATE_NOTIFICATION_SET): {
|
||||
owner->handleChangedDataset(sid);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
case (HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE): {
|
||||
gp_id_t globPoolId = HousekeepingMessage::getUpdateNotificationVariableCommand(message);
|
||||
owner->handleChangedPoolVariable(globPoolId);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
case (HousekeepingMessage::UPDATE_SNAPSHOT_SET): {
|
||||
store_address_t storeId;
|
||||
HousekeepingMessage::getUpdateSnapshotSetCommand(message, &storeId);
|
||||
bool clearMessage = true;
|
||||
owner->handleChangedDataset(sid, storeId, &clearMessage);
|
||||
if (clearMessage) {
|
||||
message->clear();
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
case (HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE): {
|
||||
store_address_t storeId;
|
||||
gp_id_t globPoolId = HousekeepingMessage::getUpdateSnapshotVariableCommand(message, &storeId);
|
||||
bool clearMessage = true;
|
||||
owner->handleChangedPoolVariable(globPoolId, storeId, &clearMessage);
|
||||
if (clearMessage) {
|
||||
message->clear();
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
default:
|
||||
return CommandMessageIF::UNKNOWN_COMMAND;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
hkQueue->sendMessage(hkDestinationId, &reply);
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::printPoolEntry(lp_id_t localPoolId) {
|
||||
auto poolIter = localPoolMap.find(localPoolId);
|
||||
if (poolIter == localPoolMap.end()) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "printPoolEntry",
|
||||
localpool::POOL_ENTRY_NOT_FOUND);
|
||||
return localpool::POOL_ENTRY_NOT_FOUND;
|
||||
}
|
||||
poolIter->second->print();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
MutexIF* LocalDataPoolManager::getMutexHandle() { return mutex; }
|
||||
|
||||
HasLocalDataPoolIF* LocalDataPoolManager::getOwner() { return owner; }
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
|
||||
LocalPoolDataSetBase* dataSet,
|
||||
bool forDownlink,
|
||||
MessageQueueId_t destination) {
|
||||
if (dataSet == nullptr) {
|
||||
/* Configuration error. */
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateHousekeepingPacket",
|
||||
DATASET_NOT_FOUND);
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
|
||||
store_address_t storeId;
|
||||
HousekeepingPacketDownlink hkPacket(sid, dataSet);
|
||||
size_t serializedSize = 0;
|
||||
ReturnValue_t result =
|
||||
serializeHkPacketIntoStore(hkPacket, storeId, forDownlink, &serializedSize);
|
||||
if (result != returnvalue::OK or serializedSize == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Now we set a HK message and send it the HK packet destination. */
|
||||
CommandMessage hkMessage;
|
||||
HousekeepingMessage::setHkReportReply(&hkMessage, sid, storeId);
|
||||
|
||||
if (hkQueue == nullptr) {
|
||||
/* Error, no queue available to send packet with. */
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateHousekeepingPacket",
|
||||
QUEUE_OR_DESTINATION_INVALID);
|
||||
return QUEUE_OR_DESTINATION_INVALID;
|
||||
}
|
||||
if (destination == MessageQueueIF::NO_QUEUE) {
|
||||
if (hkDestinationId == MessageQueueIF::NO_QUEUE) {
|
||||
/* Error, all destinations invalid */
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateHousekeepingPacket",
|
||||
QUEUE_OR_DESTINATION_INVALID);
|
||||
return QUEUE_OR_DESTINATION_INVALID;
|
||||
}
|
||||
destination = hkDestinationId;
|
||||
}
|
||||
|
||||
return hkQueue->sendMessage(destination, &hkMessage);
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::serializeHkPacketIntoStore(HousekeepingPacketDownlink& hkPacket,
|
||||
store_address_t& storeId,
|
||||
bool forDownlink,
|
||||
size_t* serializedSize) {
|
||||
uint8_t* dataPtr = nullptr;
|
||||
const size_t maxSize = hkPacket.getSerializedSize();
|
||||
ReturnValue_t result = ipcStore->getFreeElement(&storeId, maxSize, &dataPtr);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (forDownlink) {
|
||||
return hkPacket.serialize(&dataPtr, serializedSize, maxSize, SerializeIF::Endianness::BIG);
|
||||
}
|
||||
return hkPacket.serialize(&dataPtr, serializedSize, maxSize, SerializeIF::Endianness::MACHINE);
|
||||
}
|
||||
|
||||
void LocalDataPoolManager::performPeriodicHkGeneration(HkReceiver& receiver) {
|
||||
sid_t sid = receiver.dataId.sid;
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||
if (dataSet == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "performPeriodicHkGeneration",
|
||||
DATASET_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (not LocalPoolDataSetAttorney::getReportingEnabled(*dataSet)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PeriodicHousekeepingHelper* periodicHelper =
|
||||
LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet);
|
||||
|
||||
if (periodicHelper == nullptr) {
|
||||
/* Configuration error */
|
||||
return;
|
||||
}
|
||||
|
||||
if (not periodicHelper->checkOpNecessary()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReturnValue_t result = generateHousekeepingPacket(sid, dataSet, true);
|
||||
if (result != returnvalue::OK) {
|
||||
/* Configuration error */
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "LocalDataPoolManager::performPeriodicHkOperation: HK generation failed."
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning("LocalDataPoolManager::performPeriodicHkOperation: HK generation failed.\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid, bool enable) {
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||
if (dataSet == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "togglePeriodicGeneration",
|
||||
DATASET_NOT_FOUND);
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
|
||||
if ((LocalPoolDataSetAttorney::getReportingEnabled(*dataSet) and enable) or
|
||||
(not LocalPoolDataSetAttorney::getReportingEnabled(*dataSet) and not enable)) {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, enable);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
|
||||
float newCollectionInterval) {
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||
if (dataSet == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "changeCollectionInterval",
|
||||
DATASET_NOT_FOUND);
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
|
||||
PeriodicHousekeepingHelper* periodicHelper =
|
||||
LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet);
|
||||
|
||||
if (periodicHelper == nullptr) {
|
||||
/* Configuration error, set might not have a corresponding pool manager */
|
||||
return PERIODIC_HELPER_INVALID;
|
||||
}
|
||||
|
||||
periodicHelper->changeCollectionInterval(newCollectionInterval);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid) {
|
||||
/* Get and check dataset first. */
|
||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||
if (dataSet == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "performPeriodicHkGeneration",
|
||||
DATASET_NOT_FOUND);
|
||||
return DATASET_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool valid = dataSet->isValid();
|
||||
bool reportingEnabled = LocalPoolDataSetAttorney::getReportingEnabled(*dataSet);
|
||||
float collectionInterval =
|
||||
LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet)->getCollectionIntervalInSeconds();
|
||||
|
||||
// Generate set packet which can be serialized.
|
||||
HousekeepingSetPacket setPacket(sid, reportingEnabled, valid, collectionInterval, dataSet);
|
||||
size_t expectedSize = setPacket.getSerializedSize();
|
||||
uint8_t* storePtr = nullptr;
|
||||
store_address_t storeId;
|
||||
ReturnValue_t result = ipcStore->getFreeElement(&storeId, expectedSize, &storePtr);
|
||||
if (result != returnvalue::OK) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_ERROR, "generateSetStructurePacket",
|
||||
returnvalue::FAILED, "Could not get free element from IPC store.");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Serialize set packet into store.
|
||||
size_t size = 0;
|
||||
result = setPacket.serialize(&storePtr, &size, expectedSize, SerializeIF::Endianness::BIG);
|
||||
if (result != returnvalue::OK) {
|
||||
ipcStore->deleteData(storeId);
|
||||
return result;
|
||||
}
|
||||
if (expectedSize != size) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "generateSetStructurePacket",
|
||||
returnvalue::FAILED, "Expected size is not equal to serialized size");
|
||||
}
|
||||
|
||||
// Send structure reporting reply.
|
||||
CommandMessage reply;
|
||||
HousekeepingMessage::setHkStuctureReportReply(&reply, sid, storeId);
|
||||
|
||||
result = hkQueue->reply(&reply);
|
||||
if (result != returnvalue::OK) {
|
||||
ipcStore->deleteData(storeId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void LocalDataPoolManager::clearReceiversList() {
|
||||
/* Clear the vector completely and releases allocated memory. */
|
||||
HkReceivers().swap(hkReceivers);
|
||||
/* Also clear the reset helper if it exists */
|
||||
HkUpdateResetList().swap(hkUpdateResetList);
|
||||
}
|
||||
|
||||
MutexIF* LocalDataPoolManager::getLocalPoolMutex() { return this->mutex; }
|
||||
|
||||
object_id_t LocalDataPoolManager::getCreatorObjectId() const { return owner->getObjectId(); }
|
||||
|
||||
void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
|
||||
const char* functionName, ReturnValue_t error,
|
||||
const char* errorPrint) {
|
||||
#if FSFW_VERBOSE_LEVEL >= 1
|
||||
if (errorPrint == nullptr) {
|
||||
if (error == DATASET_NOT_FOUND) {
|
||||
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";
|
||||
} else {
|
||||
errorPrint = "Generic error";
|
||||
}
|
||||
} else if (error == QUEUE_OR_DESTINATION_INVALID) {
|
||||
errorPrint = "Queue or destination not set";
|
||||
} else if (error == localpool::POOL_ENTRY_TYPE_CONFLICT) {
|
||||
errorPrint = "Pool entry type conflict";
|
||||
} else if (error == localpool::POOL_ENTRY_NOT_FOUND) {
|
||||
errorPrint = "Pool entry not found";
|
||||
} else {
|
||||
errorPrint = "Unknown error";
|
||||
}
|
||||
}
|
||||
object_id_t objectId = 0xffffffff;
|
||||
if (owner != nullptr) {
|
||||
objectId = owner->getObjectId();
|
||||
}
|
||||
|
||||
if (outputType == sif::OutputTypes::OUT_WARNING) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "LocalDataPoolManager::" << functionName << ": Object ID 0x" << std::setw(8)
|
||||
<< std::setfill('0') << std::hex << objectId << " | " << errorPrint << std::dec
|
||||
<< std::setfill(' ') << std::endl;
|
||||
#else
|
||||
sif::printWarning("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n", functionName, objectId,
|
||||
errorPrint);
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
} else if (outputType == sif::OutputTypes::OUT_ERROR) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "LocalDataPoolManager::" << functionName << ": Object ID 0x" << std::setw(8)
|
||||
<< std::setfill('0') << std::hex << objectId << " | " << errorPrint << std::dec
|
||||
<< std::setfill(' ') << std::endl;
|
||||
#else
|
||||
sif::printError("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n", functionName, objectId,
|
||||
errorPrint);
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
}
|
||||
#endif /* #if FSFW_VERBOSE_LEVEL >= 1 */
|
||||
}
|
||||
|
||||
LocalDataPoolManager* LocalDataPoolManager::getPoolManagerHandle() { return this; }
|
||||
|
||||
void LocalDataPoolManager::setHkDestinationId(MessageQueueId_t hkDestId) {
|
||||
hkDestinationId = hkDestId;
|
||||
}
|
||||
@@ -1,380 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_
|
||||
#define FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "AccessLocalPoolF.h"
|
||||
#include "ProvidesDataPoolSubscriptionIF.h"
|
||||
#include "fsfw/datapool/DataSetIF.h"
|
||||
#include "fsfw/datapool/PoolEntry.h"
|
||||
#include "fsfw/housekeeping/AcceptsHkPacketsIF.h"
|
||||
#include "fsfw/housekeeping/HousekeepingMessage.h"
|
||||
#include "fsfw/housekeeping/HousekeepingPacketDownlink.h"
|
||||
#include "fsfw/housekeeping/PeriodicHousekeepingHelper.h"
|
||||
#include "fsfw/ipc/CommandMessage.h"
|
||||
#include "fsfw/ipc/MessageQueueIF.h"
|
||||
#include "fsfw/ipc/MutexGuard.h"
|
||||
#include "fsfw/ipc/MutexIF.h"
|
||||
#include "fsfw/objectmanager/SystemObjectIF.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
|
||||
namespace Factory {
|
||||
void setStaticFrameworkObjectIds();
|
||||
}
|
||||
|
||||
class LocalPoolDataSetBase;
|
||||
class HousekeepingSnapshot;
|
||||
class HasLocalDataPoolIF;
|
||||
class LocalDataPool;
|
||||
|
||||
/**
|
||||
* @brief This class is the managing instance for the local data pool.
|
||||
* @details
|
||||
* The actual data pool structure is a member of this class. Any class which
|
||||
* has a local data pool shall have this manager class as a member and implement
|
||||
* the HasLocalDataPoolIF.
|
||||
*
|
||||
* The manager offers some adaption points and functions which can be used
|
||||
* by the owning class to simplify data handling significantly.
|
||||
*
|
||||
* Please ensure that both initialize and initializeAfterTaskCreation are
|
||||
* called at some point by the owning class in the respective functions of the
|
||||
* same name!
|
||||
*
|
||||
* Users of the data pool use the helper classes LocalDataSet,
|
||||
* LocalPoolVariable and LocalPoolVector to access pool entries in
|
||||
* a thread-safe and efficient way.
|
||||
*
|
||||
* The local data pools employ a blackboard logic: Only the most recent
|
||||
* value is stored. The helper classes offer a read() and commit() interface
|
||||
* through the PoolVariableIF which is used to read and update values.
|
||||
* Each pool entry has a valid state too.
|
||||
* @author R. Mueller
|
||||
*/
|
||||
class LocalDataPoolManager : public ProvidesDataPoolSubscriptionIF, public AccessPoolManagerIF {
|
||||
friend void(Factory::setStaticFrameworkObjectIds)();
|
||||
//! Some classes using the pool manager directly need to access class internals of the
|
||||
//! manager. The attorney provides granular control of access to these internals.
|
||||
friend class LocalDpManagerAttorney;
|
||||
|
||||
public:
|
||||
static constexpr uint8_t INTERFACE_ID = CLASS_ID::HOUSEKEEPING_MANAGER;
|
||||
|
||||
static constexpr ReturnValue_t QUEUE_OR_DESTINATION_INVALID = MAKE_RETURN_CODE(0);
|
||||
static constexpr ReturnValue_t WRONG_HK_PACKET_TYPE = MAKE_RETURN_CODE(1);
|
||||
static constexpr ReturnValue_t REPORTING_STATUS_UNCHANGED = MAKE_RETURN_CODE(2);
|
||||
static constexpr ReturnValue_t PERIODIC_HELPER_INVALID = MAKE_RETURN_CODE(3);
|
||||
static constexpr ReturnValue_t POOLOBJECT_NOT_FOUND = MAKE_RETURN_CODE(4);
|
||||
static constexpr ReturnValue_t DATASET_NOT_FOUND = MAKE_RETURN_CODE(5);
|
||||
|
||||
/**
|
||||
* This constructor is used by a class which wants to implement
|
||||
* a personal local data pool. The queueToUse can be supplied if it
|
||||
* is already known.
|
||||
*
|
||||
* initialize() has to be called in any case before using the object!
|
||||
* @param owner
|
||||
* @param queueToUse
|
||||
* @param appendValidityBuffer Specify whether a buffer containing the
|
||||
* validity state is generated when serializing or deserializing packets.
|
||||
*/
|
||||
LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQueueIF* queueToUse,
|
||||
bool appendValidityBuffer = true);
|
||||
~LocalDataPoolManager() override;
|
||||
|
||||
void setHkDestinationId(MessageQueueId_t hkDestId);
|
||||
|
||||
/**
|
||||
* Assigns the queue to use. Make sure to call this in the #initialize
|
||||
* function of the owner.
|
||||
* @param queueToUse
|
||||
* @param nonDiagInvlFactor See #setNonDiagnosticIntervalFactor doc
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t initialize(MessageQueueIF* queueToUse);
|
||||
|
||||
/**
|
||||
* Initializes the map by calling the map initialization function and
|
||||
* setting the periodic factor for non-diagnostic packets.
|
||||
* Don't forget to call this in the #initializeAfterTaskCreation call of
|
||||
* the owner, otherwise the map will be invalid!
|
||||
* @param nonDiagInvlFactor
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t initializeAfterTaskCreation();
|
||||
|
||||
/**
|
||||
* @brief This should be called in the periodic handler of the owner.
|
||||
* @details
|
||||
* This in generally called in the #performOperation function of the owner.
|
||||
* It performs all the periodic functionalities of the data pool manager,
|
||||
* for example generating periodic HK packets.
|
||||
* Marked virtual as an adaption point for custom data pool managers.
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t performHkOperation();
|
||||
|
||||
/**
|
||||
* @brief Subscribe for a notification message which will be sent
|
||||
* if a dataset has changed.
|
||||
* @details
|
||||
* This subscription mechanism will generally be used internally by
|
||||
* other software components.
|
||||
* @param setId Set ID of the set to receive update messages from.
|
||||
* @param destinationObject
|
||||
* @param targetQueueId
|
||||
* @param generateSnapshot If this is set to true, a copy of the current
|
||||
* data with a timestamp will be generated and sent via message.
|
||||
* Otherwise, only an notification message is sent.
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t subscribeForSetUpdateMessage(uint32_t setId, object_id_t destinationObject,
|
||||
MessageQueueId_t targetQueueId,
|
||||
bool generateSnapshot) override;
|
||||
|
||||
/**
|
||||
* @brief Subscribe for an notification message which will be sent if a
|
||||
* pool variable has changed.
|
||||
* @details
|
||||
* This subscription mechanism will generally be used internally by
|
||||
* other software components.
|
||||
* @param localPoolId Pool ID of the pool variable
|
||||
* @param destinationObject
|
||||
* @param targetQueueId
|
||||
* @param generateSnapshot If this is set to true, a copy of the current
|
||||
* data with a timestamp will be generated and sent via message.
|
||||
* Otherwise, only an notification message is sent.
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t subscribeForVariableUpdateMessage(lp_id_t localPoolId,
|
||||
object_id_t destinationObject,
|
||||
MessageQueueId_t targetQueueId,
|
||||
bool generateSnapshot) override;
|
||||
|
||||
/**
|
||||
* @brief The manager is also able to handle housekeeping messages.
|
||||
* @details
|
||||
* This most commonly is used to handle messages for the housekeeping
|
||||
* interface, but the manager is also able to handle update notifications
|
||||
* and calls a special function which can be overriden by a child class
|
||||
* to handle data set or pool variable updates. This is relevant
|
||||
* for classes like controllers which have their own local datapool
|
||||
* but pull their data from other local datapools.
|
||||
* @param message
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t handleHousekeepingMessage(CommandMessage* message);
|
||||
|
||||
/**
|
||||
* Generate a housekeeping packet with a given SID.
|
||||
* @param sid
|
||||
* @return
|
||||
*/
|
||||
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();
|
||||
|
||||
ReturnValue_t printPoolEntry(lp_id_t localPoolId);
|
||||
|
||||
/**
|
||||
* Different types of housekeeping reporting are possible.
|
||||
* 1. PERIODIC:
|
||||
* HK packets are generated in fixed intervals and sent to
|
||||
* destination. Fromat will be raw.
|
||||
* 2. UPDATE_NOTIFICATION:
|
||||
* Notification will be sent out if HK data has changed.
|
||||
* 3. UPDATE_SNAPSHOT:
|
||||
* HK packets are only generated if explicitely requested.
|
||||
* Propably not necessary, just use multiple local data sets or
|
||||
* shared datasets.
|
||||
*/
|
||||
enum class ReportingType : uint8_t {
|
||||
//! Periodic generation of HK packets.
|
||||
PERIODIC,
|
||||
//! Housekeeping packet will be generated if values have changed.
|
||||
UPDATE_HK,
|
||||
//! Update notification will be sent out as message.
|
||||
UPDATE_NOTIFICATION,
|
||||
//! Notification will be sent out as message and a snapshot of the
|
||||
//! current data will be generated.
|
||||
UPDATE_SNAPSHOT,
|
||||
};
|
||||
|
||||
/** Different data types are possible in the HK receiver map. For example, updates can be
|
||||
requested for full datasets or for single pool variables. Periodic reporting is only possible
|
||||
for data sets. */
|
||||
enum class DataType : uint8_t { LOCAL_POOL_VARIABLE, DATA_SET };
|
||||
|
||||
/* Copying forbidden */
|
||||
LocalDataPoolManager(const LocalDataPoolManager&) = delete;
|
||||
LocalDataPoolManager operator=(const LocalDataPoolManager&) = delete;
|
||||
|
||||
/**
|
||||
* This function can be used to clear the receivers list. This is
|
||||
* intended for test functions and not for regular operations, because
|
||||
* the insertion operations allocate dynamically.
|
||||
*/
|
||||
void clearReceiversList();
|
||||
|
||||
[[nodiscard]] object_id_t getCreatorObjectId() const;
|
||||
|
||||
/**
|
||||
* Get the pointer to the mutex. Can be used to lock the data pool
|
||||
* externally. Use with care and don't forget to unlock locked mutexes!
|
||||
* For now, only friend classes can accss this function.
|
||||
* @return
|
||||
*/
|
||||
MutexIF* getMutexHandle();
|
||||
|
||||
LocalDataPoolManager* getPoolManagerHandle() override;
|
||||
ReturnValue_t subscribeForRegularPeriodicPacket(subdp::RegularHkPeriodicParams params) override;
|
||||
ReturnValue_t subscribeForDiagPeriodicPacket(subdp::DiagnosticsHkPeriodicParams params) override;
|
||||
|
||||
ReturnValue_t subscribeForRegularUpdatePacket(subdp::RegularHkUpdateParams params) override;
|
||||
ReturnValue_t subscribeForDiagUpdatePacket(subdp::DiagnosticsHkUpdateParams params) override;
|
||||
|
||||
protected:
|
||||
ReturnValue_t subscribeForPeriodicPacket(subdp::ParamsBase& params);
|
||||
ReturnValue_t subscribeForUpdatePacket(subdp::ParamsBase& params);
|
||||
|
||||
/** Core data structure for the actual pool data */
|
||||
localpool::DataPool localPoolMap;
|
||||
/** Every housekeeping data manager has a mutex to protect access
|
||||
to it's data pool. */
|
||||
MutexIF* mutex = nullptr;
|
||||
|
||||
/** The class which actually owns the manager (and its datapool). */
|
||||
HasLocalDataPoolIF* owner = nullptr;
|
||||
|
||||
uint8_t nonDiagnosticIntervalFactor = 0;
|
||||
|
||||
/** Default receiver for periodic HK packets */
|
||||
static object_id_t defaultHkDestination;
|
||||
MessageQueueId_t hkDestinationId = MessageQueueIF::NO_QUEUE;
|
||||
|
||||
union DataId {
|
||||
DataId() : sid() {};
|
||||
sid_t sid;
|
||||
lp_id_t localPoolId;
|
||||
};
|
||||
|
||||
/** The data pool manager will keep an internal map of HK receivers. */
|
||||
struct HkReceiver {
|
||||
/** Object ID of receiver */
|
||||
object_id_t objectId = objects::NO_OBJECT;
|
||||
|
||||
DataType dataType = DataType::DATA_SET;
|
||||
DataId dataId;
|
||||
|
||||
ReportingType reportingType = ReportingType::PERIODIC;
|
||||
MessageQueueId_t destinationQueue = MessageQueueIF::NO_QUEUE;
|
||||
};
|
||||
|
||||
/** This vector will contain the list of HK receivers. */
|
||||
using HkReceivers = std::vector<struct HkReceiver>;
|
||||
|
||||
HkReceivers hkReceivers;
|
||||
|
||||
struct HkUpdateResetHelper {
|
||||
DataType dataType = DataType::DATA_SET;
|
||||
DataId dataId;
|
||||
uint8_t updateCounter;
|
||||
uint8_t currentUpdateCounter;
|
||||
};
|
||||
|
||||
using HkUpdateResetList = std::vector<struct HkUpdateResetHelper>;
|
||||
/** This list is used to manage creating multiple update packets and only resetting
|
||||
the update flag if all of them were created. */
|
||||
HkUpdateResetList hkUpdateResetList = HkUpdateResetList();
|
||||
|
||||
/** This is the map holding the actual data. Should only be initialized
|
||||
* once ! */
|
||||
bool mapInitialized = false;
|
||||
/** This specifies whether a validity buffer is appended at the end
|
||||
* of generated housekeeping packets. */
|
||||
bool appendValidityBuffer = true;
|
||||
|
||||
/**
|
||||
* @brief Queue used for communication, for example commands.
|
||||
* Is also used to send messages. Can be set either in the constructor
|
||||
* or in the initialize() function.
|
||||
*/
|
||||
MessageQueueIF* hkQueue = nullptr;
|
||||
|
||||
/** Global IPC store is used to store all packets. */
|
||||
StorageManagerIF* ipcStore = nullptr;
|
||||
|
||||
/**
|
||||
* Read a variable by supplying its local pool ID and assign the pool
|
||||
* entry to the supplied PoolEntry pointer. The type of the pool entry
|
||||
* is deduced automatically. This call is not thread-safe!
|
||||
* For now, only classes designated by the LocalDpManagerAttorney may use this function.
|
||||
* @tparam T Type of the pool entry
|
||||
* @param localPoolId Pool ID of the variable to read
|
||||
* @param poolVar [out] Corresponding pool entry will be assigned to the
|
||||
* supplied pointer.
|
||||
* @return
|
||||
*/
|
||||
template <class T>
|
||||
ReturnValue_t fetchPoolEntry(lp_id_t localPoolId, PoolEntry<T>** poolEntry);
|
||||
|
||||
/**
|
||||
* This function is used to fill the local data pool map with pool
|
||||
* entries. It should only be called once by the pool owner.
|
||||
* @param localDataPoolMap
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t initializeHousekeepingPoolEntriesOnce();
|
||||
|
||||
MutexIF* getLocalPoolMutex() override;
|
||||
|
||||
ReturnValue_t serializeHkPacketIntoStore(HousekeepingPacketDownlink& hkPacket,
|
||||
store_address_t& storeId, bool forDownlink,
|
||||
size_t* serializedSize);
|
||||
|
||||
void performPeriodicHkGeneration(HkReceiver& hkReceiver);
|
||||
ReturnValue_t togglePeriodicGeneration(sid_t sid, bool enable);
|
||||
ReturnValue_t generateSetStructurePacket(sid_t sid);
|
||||
|
||||
void handleHkUpdateResetListInsertion(DataType dataType, DataId dataId);
|
||||
void handleChangeResetLogic(DataType type, DataId dataId, MarkChangedIF* toReset);
|
||||
void resetHkUpdateResetHelper();
|
||||
|
||||
ReturnValue_t handleHkUpdate(HkReceiver& hkReceiver, ReturnValue_t& status);
|
||||
ReturnValue_t handleNotificationUpdate(HkReceiver& hkReceiver, ReturnValue_t& status);
|
||||
ReturnValue_t handleNotificationSnapshot(HkReceiver& hkReceiver, ReturnValue_t& status);
|
||||
ReturnValue_t addUpdateToStore(HousekeepingSnapshot& updatePacket, store_address_t& storeId);
|
||||
|
||||
void printWarningOrError(sif::OutputTypes outputType, const char* functionName,
|
||||
ReturnValue_t errorCode = returnvalue::FAILED,
|
||||
const char* errorPrint = nullptr);
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId,
|
||||
PoolEntry<T>** poolEntry) {
|
||||
if (poolEntry == nullptr) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
|
||||
auto poolIter = localPoolMap.find(localPoolId);
|
||||
if (poolIter == localPoolMap.end()) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry",
|
||||
localpool::POOL_ENTRY_NOT_FOUND);
|
||||
return localpool::POOL_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
*poolEntry = dynamic_cast<PoolEntry<T>*>(poolIter->second);
|
||||
if (*poolEntry == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry",
|
||||
localpool::POOL_ENTRY_TYPE_CONFLICT);
|
||||
return localpool::POOL_ENTRY_TYPE_CONFLICT;
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_ */
|
||||
@@ -1,21 +0,0 @@
|
||||
#include "fsfw/datapoollocal/LocalDataSet.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
|
||||
#include "fsfw/serialize/SerializeAdapter.h"
|
||||
|
||||
LocalDataSet::LocalDataSet(HasLocalDataPoolIF *hkOwner, uint32_t setId,
|
||||
const size_t maxNumberOfVariables)
|
||||
: LocalPoolDataSetBase(hkOwner, setId, nullptr, maxNumberOfVariables),
|
||||
poolVarList(maxNumberOfVariables) {
|
||||
this->setContainer(poolVarList.data());
|
||||
}
|
||||
|
||||
LocalDataSet::LocalDataSet(sid_t sid, const size_t maxNumberOfVariables)
|
||||
: LocalPoolDataSetBase(sid, nullptr, maxNumberOfVariables), poolVarList(maxNumberOfVariables) {
|
||||
this->setContainer(poolVarList.data());
|
||||
}
|
||||
|
||||
LocalDataSet::~LocalDataSet() {}
|
||||
@@ -1,291 +0,0 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "fsfw/datapoollocal.h"
|
||||
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
|
||||
#include "fsfw/globalfunctions/bitutility.h"
|
||||
#include "fsfw/housekeeping/PeriodicHousekeepingHelper.h"
|
||||
#include "fsfw/objectmanager/ObjectManager.h"
|
||||
#include "fsfw/serialize/SerializeAdapter.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
#include "internal/HasLocalDpIFUserAttorney.h"
|
||||
|
||||
LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner, uint32_t setId,
|
||||
PoolVariableIF **registeredVariablesArray,
|
||||
const size_t maxNumberOfVariables, bool periodicHandling)
|
||||
: PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
|
||||
if (hkOwner == nullptr) {
|
||||
// Configuration error.
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::error << "LocalPoolDataSetBase::LocalPoolDataSetBase: Owner "
|
||||
<< "invalid!" << std::endl;
|
||||
#else
|
||||
sif::printError(
|
||||
"LocalPoolDataSetBase::LocalPoolDataSetBase: Owner "
|
||||
"invalid!\n\r");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
return;
|
||||
}
|
||||
AccessPoolManagerIF *accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
|
||||
|
||||
if (accessor != nullptr) {
|
||||
poolManager = accessor->getPoolManagerHandle();
|
||||
mutexIfSingleDataCreator = accessor->getLocalPoolMutex();
|
||||
}
|
||||
|
||||
this->sid.objectId = hkOwner->getObjectId();
|
||||
this->sid.ownerSetId = setId;
|
||||
|
||||
/* Data creators get a periodic helper for periodic HK data generation. */
|
||||
if (periodicHandling) {
|
||||
periodicHelper = new PeriodicHousekeepingHelper(this);
|
||||
}
|
||||
}
|
||||
|
||||
LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF **registeredVariablesArray,
|
||||
const size_t maxNumberOfVariables)
|
||||
: PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
|
||||
HasLocalDataPoolIF *hkOwner = ObjectManager::instance()->get<HasLocalDataPoolIF>(sid.objectId);
|
||||
if (hkOwner != nullptr) {
|
||||
AccessPoolManagerIF *accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
|
||||
if (accessor != nullptr) {
|
||||
mutexIfSingleDataCreator = accessor->getLocalPoolMutex();
|
||||
poolManager = accessor->getPoolManagerHandle();
|
||||
}
|
||||
}
|
||||
|
||||
this->sid = sid;
|
||||
}
|
||||
|
||||
LocalPoolDataSetBase::LocalPoolDataSetBase(PoolVariableIF **registeredVariablesArray,
|
||||
const size_t maxNumberOfVariables,
|
||||
bool protectEveryReadCommitCall)
|
||||
: PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
|
||||
this->setReadCommitProtectionBehaviour(protectEveryReadCommitCall);
|
||||
}
|
||||
|
||||
LocalPoolDataSetBase::~LocalPoolDataSetBase() {
|
||||
/* We only delete objects which were created in the class constructor */
|
||||
if (periodicHelper != nullptr) {
|
||||
delete periodicHelper;
|
||||
}
|
||||
/* In case set was read but not comitted, we commit all variables with an invalid state */
|
||||
if (state == States::STATE_SET_WAS_READ) {
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
if (registeredVariables[count] != nullptr) {
|
||||
registeredVariables[count]->setValid(false);
|
||||
registeredVariables[count]->commit(MutexIF::TimeoutType::WAITING, 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t LocalPoolDataSetBase::lockDataPool(MutexIF::TimeoutType timeoutType,
|
||||
uint32_t timeoutMs) {
|
||||
if (mutexIfSingleDataCreator != nullptr) {
|
||||
return mutexIfSingleDataCreator->lockMutex(timeoutType, timeoutMs);
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(
|
||||
uint8_t **buffer, size_t *size, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
ReturnValue_t result = returnvalue::OK;
|
||||
const uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount) / 8.0);
|
||||
uint8_t *validityPtr = nullptr;
|
||||
#if defined(_MSC_VER) || defined(__clang__)
|
||||
// Use a std::vector here because MSVC will (rightly) not create a fixed size array
|
||||
// with a non constant size specifier. The Apple compiler (LLVM) will not accept
|
||||
// the initialization of a variable sized array
|
||||
std::vector<uint8_t> validityMask(validityMaskSize, 0);
|
||||
validityPtr = validityMask.data();
|
||||
#else
|
||||
uint8_t validityMask[validityMaskSize] = {};
|
||||
validityPtr = validityMask;
|
||||
#endif
|
||||
uint8_t validBufferIndex = 0;
|
||||
uint8_t validBufferIndexBit = 0;
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
if (registeredVariables[count]->isValid()) {
|
||||
/* Set bit at correct position */
|
||||
bitutil::set(validityPtr + validBufferIndex, validBufferIndexBit);
|
||||
}
|
||||
if (validBufferIndexBit == 7) {
|
||||
validBufferIndex++;
|
||||
validBufferIndexBit = 0;
|
||||
} else {
|
||||
validBufferIndexBit++;
|
||||
}
|
||||
|
||||
result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (*size + validityMaskSize > maxSize) {
|
||||
return SerializeIF::BUFFER_TOO_SHORT;
|
||||
}
|
||||
// copy validity buffer to end
|
||||
std::memcpy(*buffer, validityPtr, validityMaskSize);
|
||||
*size += validityMaskSize;
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer(
|
||||
const uint8_t **buffer, size_t *size, SerializeIF::Endianness streamEndianness) {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
result = registeredVariables[count]->deSerialize(buffer, size, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (*size < std::ceil(static_cast<float>(fillCount) / 8.0)) {
|
||||
return SerializeIF::STREAM_TOO_SHORT;
|
||||
}
|
||||
|
||||
uint8_t validBufferIndex = 0;
|
||||
uint8_t validBufferIndexBit = 0;
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
// set validity buffer here.
|
||||
bool nextVarValid = false;
|
||||
bitutil::get(*buffer + validBufferIndex, validBufferIndexBit, nextVarValid);
|
||||
registeredVariables[count]->setValid(nextVarValid);
|
||||
|
||||
if (validBufferIndexBit == 7) {
|
||||
validBufferIndex++;
|
||||
validBufferIndexBit = 0;
|
||||
} else {
|
||||
validBufferIndexBit++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalPoolDataSetBase::unlockDataPool() {
|
||||
if (mutexIfSingleDataCreator != nullptr) {
|
||||
return mutexIfSingleDataCreator->unlockMutex();
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t **buffer, size_t *size,
|
||||
size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness,
|
||||
bool serializeFillCount) const {
|
||||
/* Serialize fill count as uint8_t */
|
||||
uint8_t fillCount = this->fillCount;
|
||||
if (serializeFillCount) {
|
||||
SerializeAdapter::serialize(&fillCount, buffer, size, maxSize, streamEndianness);
|
||||
}
|
||||
for (uint16_t count = 0; count < fillCount; count++) {
|
||||
lp_id_t currentPoolId = registeredVariables[count]->getDataPoolId();
|
||||
auto result =
|
||||
SerializeAdapter::serialize(¤tPoolId, buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "LocalPoolDataSetBase::serializeLocalPoolIds: "
|
||||
<< "Serialization error!" << std::endl;
|
||||
#else
|
||||
sif::printWarning(
|
||||
"LocalPoolDataSetBase::serializeLocalPoolIds: "
|
||||
"Serialization error!\n\r");
|
||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
uint8_t LocalPoolDataSetBase::getLocalPoolIdsSerializedSize(bool serializeFillCount) const {
|
||||
if (serializeFillCount) {
|
||||
return fillCount * sizeof(lp_id_t) + sizeof(uint8_t);
|
||||
} else {
|
||||
return fillCount * sizeof(lp_id_t);
|
||||
}
|
||||
}
|
||||
|
||||
size_t LocalPoolDataSetBase::getSerializedSize() const {
|
||||
if (withValidityBuffer) {
|
||||
uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount) / 8.0);
|
||||
return validityMaskSize + PoolDataSetBase::getSerializedSize();
|
||||
} else {
|
||||
return PoolDataSetBase::getSerializedSize();
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPoolDataSetBase::setValidityBufferGeneration(bool withValidityBuffer) {
|
||||
this->withValidityBuffer = withValidityBuffer;
|
||||
}
|
||||
|
||||
ReturnValue_t LocalPoolDataSetBase::deSerialize(const uint8_t **buffer, size_t *size,
|
||||
SerializeIF::Endianness streamEndianness) {
|
||||
if (withValidityBuffer) {
|
||||
return this->deSerializeWithValidityBuffer(buffer, size, streamEndianness);
|
||||
} else {
|
||||
return PoolDataSetBase::deSerialize(buffer, size, streamEndianness);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue_t LocalPoolDataSetBase::serialize(uint8_t **buffer, size_t *size, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
if (withValidityBuffer) {
|
||||
return this->serializeWithValidityBuffer(buffer, size, maxSize, streamEndianness);
|
||||
} else {
|
||||
return PoolDataSetBase::serialize(buffer, size, maxSize, streamEndianness);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPoolDataSetBase::setReportingEnabled(bool reportingEnabled) {
|
||||
this->reportingEnabled = reportingEnabled;
|
||||
}
|
||||
|
||||
bool LocalPoolDataSetBase::getReportingEnabled() const { return reportingEnabled; }
|
||||
|
||||
void LocalPoolDataSetBase::initializePeriodicHelper(float collectionInterval,
|
||||
dur_millis_t minimumPeriodicInterval) {
|
||||
periodicHelper->initialize(collectionInterval, minimumPeriodicInterval);
|
||||
}
|
||||
|
||||
void LocalPoolDataSetBase::setChanged(bool changed) { this->changed = changed; }
|
||||
|
||||
bool LocalPoolDataSetBase::hasChanged() const { return changed; }
|
||||
|
||||
sid_t LocalPoolDataSetBase::getSid() const { return sid; }
|
||||
|
||||
bool LocalPoolDataSetBase::isValid() const { return this->valid; }
|
||||
|
||||
void LocalPoolDataSetBase::setValidity(bool valid, bool setEntriesRecursively) {
|
||||
if (setEntriesRecursively) {
|
||||
for (size_t idx = 0; idx < this->getFillCount(); idx++) {
|
||||
registeredVariables[idx]->setValid(valid);
|
||||
}
|
||||
}
|
||||
this->valid = valid;
|
||||
}
|
||||
|
||||
object_id_t LocalPoolDataSetBase::getCreatorObjectId() {
|
||||
if (poolManager != nullptr) {
|
||||
return poolManager->getCreatorObjectId();
|
||||
}
|
||||
return objects::NO_OBJECT;
|
||||
}
|
||||
|
||||
void LocalPoolDataSetBase::setAllVariablesReadOnly() {
|
||||
for (size_t idx = 0; idx < this->getFillCount(); idx++) {
|
||||
registeredVariables[idx]->setReadWriteMode(pool_rwm_t::VAR_READ);
|
||||
}
|
||||
}
|
||||
|
||||
float LocalPoolDataSetBase::getCollectionInterval() const {
|
||||
if (periodicHelper != nullptr) {
|
||||
return periodicHelper->getCollectionIntervalInSeconds();
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPoolDataSetBase::printSet() { return; }
|
||||
@@ -1,68 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_
|
||||
#define FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_
|
||||
|
||||
#include "MarkChangedIF.h"
|
||||
#include "fsfw/datapool/PoolVariableIF.h"
|
||||
#include "fsfw/objectmanager/SystemObjectIF.h"
|
||||
#include "fsfw/returnvalues/returnvalue.h"
|
||||
#include "localPoolDefinitions.h"
|
||||
|
||||
class LocalDataPoolManager;
|
||||
class DataSetIF;
|
||||
class HasLocalDataPoolIF;
|
||||
|
||||
/**
|
||||
* @brief This class serves as a non-template base for pool objects like pool variables
|
||||
* or pool vectors.
|
||||
*/
|
||||
class LocalPoolObjectBase : public PoolVariableIF, public MarkChangedIF {
|
||||
public:
|
||||
LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet,
|
||||
pool_rwm_t setReadWriteMode);
|
||||
|
||||
LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
|
||||
|
||||
void setReadWriteMode(pool_rwm_t newReadWriteMode) override;
|
||||
pool_rwm_t getReadWriteMode() const override;
|
||||
|
||||
bool isValid() const override;
|
||||
void setValid(bool valid) override;
|
||||
|
||||
void setChanged(bool changed) override;
|
||||
bool hasChanged() const override;
|
||||
|
||||
lp_id_t getDataPoolId() const override;
|
||||
void setDataPoolId(lp_id_t poolId);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief To access the correct data pool entry on read and commit calls,
|
||||
* the data pool id is stored.
|
||||
*/
|
||||
uint32_t localPoolId = PoolVariableIF::NO_PARAMETER;
|
||||
/**
|
||||
* @brief The valid information as it was stored in the data pool
|
||||
* is copied to this attribute.
|
||||
*/
|
||||
bool valid = false;
|
||||
|
||||
/**
|
||||
* @brief A local pool variable can be marked as changed.
|
||||
*/
|
||||
bool changed = false;
|
||||
|
||||
/**
|
||||
* @brief The information whether the class is read-write or
|
||||
* read-only is stored here.
|
||||
*/
|
||||
ReadWriteMode_t readWriteMode = pool_rwm_t::VAR_READ_WRITE;
|
||||
|
||||
//! @brief Pointer to the class which manages the HK pool.
|
||||
LocalDataPoolManager* hkManager = nullptr;
|
||||
|
||||
void reportReadCommitError(const char* variableType, ReturnValue_t error, bool read,
|
||||
object_id_t objectId, lp_id_t lpId);
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_ */
|
||||
@@ -1,197 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_
|
||||
#define FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_
|
||||
|
||||
#include "../datapool/DataSetIF.h"
|
||||
#include "../datapool/PoolVariableIF.h"
|
||||
#include "../objectmanager/ObjectManagerIF.h"
|
||||
#include "../serialize/SerializeAdapter.h"
|
||||
#include "../serviceinterface/ServiceInterface.h"
|
||||
#include "AccessLocalPoolF.h"
|
||||
#include "HasLocalDataPoolIF.h"
|
||||
#include "LocalDataPoolManager.h"
|
||||
#include "LocalPoolObjectBase.h"
|
||||
#include "internal/LocalDpManagerAttorney.h"
|
||||
|
||||
/**
|
||||
* @brief Local Pool Variable class which is used to access the local pools.
|
||||
* @details
|
||||
* This class is not stored in the map. Instead, it is used to access
|
||||
* the pool entries by using a pointer to the map storing the pool
|
||||
* entries. It can also be used to organize these pool entries into data sets.
|
||||
*
|
||||
* @tparam T The template parameter sets the type of the variable. Currently,
|
||||
* all plain data types are supported, but in principle any type is possible.
|
||||
* @ingroup data_pool
|
||||
*/
|
||||
template <typename T>
|
||||
class LocalPoolVariable : public LocalPoolObjectBase {
|
||||
public:
|
||||
//! Default ctor is forbidden.
|
||||
LocalPoolVariable() = delete;
|
||||
|
||||
/**
|
||||
* This constructor is used by the data creators to have pool variable
|
||||
* instances which can also be stored in datasets.
|
||||
*
|
||||
* It does not fetch the current value from the data pool, which
|
||||
* has to be done by calling the read() operation.
|
||||
* Datasets can be used to access multiple local pool entries in an
|
||||
* efficient way. A pointer to a dataset can be passed to register
|
||||
* the pool variable in that dataset directly.
|
||||
* @param poolId ID of the local pool entry.
|
||||
* @param hkOwner Pointer of the owner. This will generally be the calling
|
||||
* class itself which passes "this".
|
||||
* @param dataSet The data set in which the variable shall register itself.
|
||||
* If nullptr, the variable is not registered.
|
||||
* @param setReadWriteMode Specify the read-write mode of the pool variable.
|
||||
*/
|
||||
LocalPoolVariable(HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
|
||||
|
||||
/**
|
||||
* This constructor is used by data users like controllers to have
|
||||
* access to the local pool variables of data creators by supplying
|
||||
* the respective creator object ID.
|
||||
*
|
||||
* It does not fetch the current value from the data pool, which
|
||||
* has to be done by calling the read() operation.
|
||||
* Datasets can be used to access multiple local pool entries in an
|
||||
* efficient way. A pointer to a dataset can be passed to register
|
||||
* the pool variable in that dataset directly.
|
||||
* @param poolId ID of the local pool entry.
|
||||
* @param hkOwner object ID of the pool owner.
|
||||
* @param dataSet The data set in which the variable shall register itself.
|
||||
* If nullptr, the variable is not registered.
|
||||
* @param setReadWriteMode Specify the read-write mode of the pool variable.
|
||||
*
|
||||
*/
|
||||
LocalPoolVariable(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
|
||||
/**
|
||||
* Variation which takes the global unique identifier of a pool variable.
|
||||
* @param globalPoolId
|
||||
* @param dataSet
|
||||
* @param setReadWriteMode
|
||||
*/
|
||||
LocalPoolVariable(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr,
|
||||
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
|
||||
|
||||
virtual ~LocalPoolVariable() {};
|
||||
|
||||
/**
|
||||
* @brief This is the local copy of the data pool entry.
|
||||
* @details The user can work on this attribute
|
||||
* just like he would on a simple local variable.
|
||||
*/
|
||||
T value = 0;
|
||||
|
||||
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const override;
|
||||
size_t getSerializedSize() const override;
|
||||
ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) override;
|
||||
|
||||
/**
|
||||
* @brief This is a call to read the array's values
|
||||
* from the global data pool.
|
||||
* @details
|
||||
* When executed, this operation tries to fetch the pool entry with matching
|
||||
* data pool id from the data pool and copies all array values and the valid
|
||||
* information to its local attributes.
|
||||
* In case of a failure (wrong type, size or pool id not found), the
|
||||
* variable is set to zero and invalid.
|
||||
* The read call is protected with a lock.
|
||||
* It is recommended to use DataSets to read and commit multiple variables
|
||||
* at once to avoid the overhead of unnecessary lock und unlock operations.
|
||||
*
|
||||
*/
|
||||
ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20) override;
|
||||
/**
|
||||
* @brief The commit call copies the array values back to the data pool.
|
||||
* @details
|
||||
* It checks type and size, as well as if the variable is writable. If so,
|
||||
* the value is copied and the local valid flag is written back as well.
|
||||
* The read call is protected with a lock.
|
||||
* It is recommended to use DataSets to read and commit multiple variables
|
||||
* at once to avoid the overhead of unnecessary lock und unlock operations.
|
||||
*/
|
||||
ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20) override;
|
||||
|
||||
/**
|
||||
* @brief This commit function can be used to set the pool variable valid
|
||||
* as well.
|
||||
* @param setValid
|
||||
* @param timeoutType
|
||||
* @param timeoutMs
|
||||
* @return
|
||||
*/
|
||||
ReturnValue_t commit(bool setValid,
|
||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
uint32_t timeoutMs = 20);
|
||||
|
||||
LocalPoolVariable<T>& operator=(const T& newValue);
|
||||
LocalPoolVariable<T>& operator=(const LocalPoolVariable<T>& newPoolVariable);
|
||||
|
||||
//! Explicit type conversion operator. Allows casting the class to
|
||||
//! its template type to perform operations on value.
|
||||
explicit operator T() const;
|
||||
|
||||
bool operator==(const LocalPoolVariable<T>& other) const;
|
||||
bool operator==(const T& other) const;
|
||||
|
||||
bool operator!=(const LocalPoolVariable<T>& other) const;
|
||||
bool operator!=(const T& other) const;
|
||||
|
||||
bool operator<(const LocalPoolVariable<T>& other) const;
|
||||
bool operator<(const T& other) const;
|
||||
|
||||
bool operator>(const LocalPoolVariable<T>& other) const;
|
||||
bool operator>(const T& other) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Like #read, but without a lock protection of the global pool.
|
||||
* @details
|
||||
* The operation does NOT provide any mutual exclusive protection by itself.
|
||||
* This can be used if the lock is handled externally to avoid the overhead
|
||||
* of consecutive lock und unlock operations.
|
||||
* Declared protected to discourage free public usage.
|
||||
*/
|
||||
ReturnValue_t readWithoutLock() override;
|
||||
/**
|
||||
* @brief Like #commit, but without a lock protection of the global pool.
|
||||
* @details
|
||||
* The operation does NOT provide any mutual exclusive protection by itself.
|
||||
* This can be used if the lock is handled externally to avoid the overhead
|
||||
* of consecutive lock und unlock operations.
|
||||
* Declared protected to discourage free public usage.
|
||||
*/
|
||||
ReturnValue_t commitWithoutLock() override;
|
||||
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
// std::ostream is the type for object std::cout
|
||||
template <typename U>
|
||||
friend std::ostream& operator<<(std::ostream& out, const LocalPoolVariable<U>& var);
|
||||
#endif
|
||||
};
|
||||
|
||||
#include "LocalPoolVariable.tpp"
|
||||
|
||||
template <class T>
|
||||
using lp_var_t = LocalPoolVariable<T>;
|
||||
|
||||
using lp_bool_t = LocalPoolVariable<uint8_t>;
|
||||
using lp_uint8_t = LocalPoolVariable<uint8_t>;
|
||||
using lp_uint16_t = LocalPoolVariable<uint16_t>;
|
||||
using lp_uint32_t = LocalPoolVariable<uint32_t>;
|
||||
using lp_uint64_t = LocalPoolVariable<uint64_t>;
|
||||
using lp_int8_t = LocalPoolVariable<int8_t>;
|
||||
using lp_int16_t = LocalPoolVariable<int16_t>;
|
||||
using lp_int32_t = LocalPoolVariable<int32_t>;
|
||||
using lp_int64_t = LocalPoolVariable<int64_t>;
|
||||
using lp_float_t = LocalPoolVariable<float>;
|
||||
using lp_double_t = LocalPoolVariable<double>;
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ */
|
||||
@@ -1,193 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_
|
||||
#define FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_
|
||||
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_
|
||||
#error Include LocalPoolVariable.h before LocalPoolVariable.tpp!
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline LocalPoolVariable<T>::LocalPoolVariable(HasLocalDataPoolIF* hkOwner, lp_id_t poolId,
|
||||
DataSetIF* dataSet, pool_rwm_t setReadWriteMode)
|
||||
: LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {}
|
||||
|
||||
template <typename T>
|
||||
inline LocalPoolVariable<T>::LocalPoolVariable(object_id_t poolOwner, lp_id_t poolId,
|
||||
DataSetIF* dataSet, pool_rwm_t setReadWriteMode)
|
||||
: LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
|
||||
|
||||
template <typename T>
|
||||
inline LocalPoolVariable<T>::LocalPoolVariable(gp_id_t globalPoolId, DataSetIF* dataSet,
|
||||
pool_rwm_t setReadWriteMode)
|
||||
: LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, dataSet,
|
||||
setReadWriteMode) {}
|
||||
|
||||
template <typename T>
|
||||
inline ReturnValue_t LocalPoolVariable<T>::read(MutexIF::TimeoutType timeoutType,
|
||||
uint32_t timeoutMs) {
|
||||
if (hkManager == nullptr) {
|
||||
return readWithoutLock();
|
||||
}
|
||||
MutexIF* mutex = LocalDpManagerAttorney::getMutexHandle(*hkManager);
|
||||
ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
result = readWithoutLock();
|
||||
mutex->unlockMutex();
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ReturnValue_t LocalPoolVariable<T>::readWithoutLock() {
|
||||
if (readWriteMode == pool_rwm_t::VAR_WRITE) {
|
||||
object_id_t targetObjectId = hkManager->getCreatorObjectId();
|
||||
reportReadCommitError("LocalPoolVector", PoolVariableIF::INVALID_READ_WRITE_MODE, true,
|
||||
targetObjectId, localPoolId);
|
||||
return PoolVariableIF::INVALID_READ_WRITE_MODE;
|
||||
}
|
||||
|
||||
PoolEntry<T>* poolEntry = nullptr;
|
||||
ReturnValue_t result =
|
||||
LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, &poolEntry);
|
||||
if (result != returnvalue::OK) {
|
||||
object_id_t ownerObjectId = hkManager->getCreatorObjectId();
|
||||
reportReadCommitError("LocalPoolVariable", result, false, ownerObjectId, localPoolId);
|
||||
return result;
|
||||
}
|
||||
|
||||
this->value = *(poolEntry->getDataPtr());
|
||||
this->valid = poolEntry->getValid();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ReturnValue_t LocalPoolVariable<T>::commit(bool setValid, MutexIF::TimeoutType timeoutType,
|
||||
uint32_t timeoutMs) {
|
||||
this->setValid(setValid);
|
||||
return commit(timeoutType, timeoutMs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ReturnValue_t LocalPoolVariable<T>::commit(MutexIF::TimeoutType timeoutType,
|
||||
uint32_t timeoutMs) {
|
||||
if (hkManager == nullptr) {
|
||||
return commitWithoutLock();
|
||||
}
|
||||
MutexIF* mutex = LocalDpManagerAttorney::getMutexHandle(*hkManager);
|
||||
ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
result = commitWithoutLock();
|
||||
mutex->unlockMutex();
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ReturnValue_t LocalPoolVariable<T>::commitWithoutLock() {
|
||||
if (readWriteMode == pool_rwm_t::VAR_READ) {
|
||||
object_id_t targetObjectId = hkManager->getCreatorObjectId();
|
||||
reportReadCommitError("LocalPoolVector", PoolVariableIF::INVALID_READ_WRITE_MODE, false,
|
||||
targetObjectId, localPoolId);
|
||||
return PoolVariableIF::INVALID_READ_WRITE_MODE;
|
||||
}
|
||||
|
||||
PoolEntry<T>* poolEntry = nullptr;
|
||||
ReturnValue_t result =
|
||||
LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, &poolEntry);
|
||||
if (result != returnvalue::OK) {
|
||||
object_id_t ownerObjectId = hkManager->getCreatorObjectId();
|
||||
reportReadCommitError("LocalPoolVariable", result, false, ownerObjectId, localPoolId);
|
||||
return result;
|
||||
}
|
||||
|
||||
*(poolEntry->getDataPtr()) = this->value;
|
||||
poolEntry->setValid(this->valid);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ReturnValue_t LocalPoolVariable<T>::serialize(
|
||||
uint8_t** buffer, size_t* size, const size_t max_size,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
return SerializeAdapter::serialize(&value, buffer, size, max_size, streamEndianness);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline size_t LocalPoolVariable<T>::getSerializedSize() const {
|
||||
return SerializeAdapter::getSerializedSize(&value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline ReturnValue_t LocalPoolVariable<T>::deSerialize(const uint8_t** buffer, size_t* size,
|
||||
SerializeIF::Endianness streamEndianness) {
|
||||
return SerializeAdapter::deSerialize(&value, buffer, size, streamEndianness);
|
||||
}
|
||||
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
template <typename T>
|
||||
inline std::ostream& operator<<(std::ostream& out, const LocalPoolVariable<T>& var) {
|
||||
out << var.value;
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline LocalPoolVariable<T>::operator T() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline LocalPoolVariable<T>& LocalPoolVariable<T>::operator=(const T& newValue) {
|
||||
value = newValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline LocalPoolVariable<T>& LocalPoolVariable<T>::operator=(
|
||||
const LocalPoolVariable<T>& newPoolVariable) {
|
||||
value = newPoolVariable.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool LocalPoolVariable<T>::operator==(const LocalPoolVariable<T>& other) const {
|
||||
return this->value == other.value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool LocalPoolVariable<T>::operator==(const T& other) const {
|
||||
return this->value == other;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool LocalPoolVariable<T>::operator!=(const LocalPoolVariable<T>& other) const {
|
||||
return not(*this == other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool LocalPoolVariable<T>::operator!=(const T& other) const {
|
||||
return not(*this == other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool LocalPoolVariable<T>::operator<(const LocalPoolVariable<T>& other) const {
|
||||
return this->value < other.value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool LocalPoolVariable<T>::operator<(const T& other) const {
|
||||
return this->value < other;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool LocalPoolVariable<T>::operator>(const LocalPoolVariable<T>& other) const {
|
||||
return not(*this < other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool LocalPoolVariable<T>::operator>(const T& other) const {
|
||||
return not(*this < other);
|
||||
}
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_ */
|
||||
@@ -1,177 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_
|
||||
#define FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_
|
||||
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_
|
||||
#error Include LocalPoolVector.h before LocalPoolVector.tpp!
|
||||
#endif
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline LocalPoolVector<T, vectorSize>::LocalPoolVector(HasLocalDataPoolIF* hkOwner, lp_id_t poolId,
|
||||
DataSetIF* dataSet,
|
||||
pool_rwm_t setReadWriteMode)
|
||||
: LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {}
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline LocalPoolVector<T, vectorSize>::LocalPoolVector(object_id_t poolOwner, lp_id_t poolId,
|
||||
DataSetIF* dataSet,
|
||||
pool_rwm_t setReadWriteMode)
|
||||
: LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline LocalPoolVector<T, vectorSize>::LocalPoolVector(gp_id_t globalPoolId, DataSetIF* dataSet,
|
||||
pool_rwm_t setReadWriteMode)
|
||||
: LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, dataSet,
|
||||
setReadWriteMode) {}
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline ReturnValue_t LocalPoolVector<T, vectorSize>::read(MutexIF::TimeoutType timeoutType,
|
||||
uint32_t timeoutMs) {
|
||||
MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
|
||||
return readWithoutLock();
|
||||
}
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline ReturnValue_t LocalPoolVector<T, vectorSize>::readWithoutLock() {
|
||||
if (readWriteMode == pool_rwm_t::VAR_WRITE) {
|
||||
object_id_t targetObjectId = hkManager->getCreatorObjectId();
|
||||
reportReadCommitError("LocalPoolVector", PoolVariableIF::INVALID_READ_WRITE_MODE, true,
|
||||
targetObjectId, localPoolId);
|
||||
return PoolVariableIF::INVALID_READ_WRITE_MODE;
|
||||
}
|
||||
|
||||
PoolEntry<T>* poolEntry = nullptr;
|
||||
ReturnValue_t result =
|
||||
LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, &poolEntry);
|
||||
memset(this->value, 0, vectorSize * sizeof(T));
|
||||
|
||||
if (result != returnvalue::OK) {
|
||||
object_id_t targetObjectId = hkManager->getCreatorObjectId();
|
||||
reportReadCommitError("LocalPoolVector", result, true, targetObjectId, localPoolId);
|
||||
return result;
|
||||
}
|
||||
std::memcpy(this->value, poolEntry->getDataPtr(), poolEntry->getByteSize());
|
||||
this->valid = poolEntry->getValid();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(bool valid,
|
||||
MutexIF::TimeoutType timeoutType,
|
||||
uint32_t timeoutMs) {
|
||||
this->setValid(valid);
|
||||
return commit(timeoutType, timeoutMs);
|
||||
}
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(MutexIF::TimeoutType timeoutType,
|
||||
uint32_t timeoutMs) {
|
||||
MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
|
||||
return commitWithoutLock();
|
||||
}
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commitWithoutLock() {
|
||||
if (readWriteMode == pool_rwm_t::VAR_READ) {
|
||||
object_id_t targetObjectId = hkManager->getCreatorObjectId();
|
||||
reportReadCommitError("LocalPoolVector", PoolVariableIF::INVALID_READ_WRITE_MODE, false,
|
||||
targetObjectId, localPoolId);
|
||||
return PoolVariableIF::INVALID_READ_WRITE_MODE;
|
||||
}
|
||||
PoolEntry<T>* poolEntry = nullptr;
|
||||
ReturnValue_t result =
|
||||
LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, &poolEntry);
|
||||
if (result != returnvalue::OK) {
|
||||
object_id_t targetObjectId = hkManager->getCreatorObjectId();
|
||||
reportReadCommitError("LocalPoolVector", result, false, targetObjectId, localPoolId);
|
||||
return result;
|
||||
}
|
||||
std::memcpy(poolEntry->getDataPtr(), this->value, poolEntry->getByteSize());
|
||||
poolEntry->setValid(this->valid);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline T& LocalPoolVector<T, vectorSize>::operator[](size_t i) {
|
||||
if (i < vectorSize) {
|
||||
return value[i];
|
||||
}
|
||||
// If this happens, I have to set some value. I consider this
|
||||
// a configuration error, but I wont exit here.
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "LocalPoolVector: Invalid index. Setting or returning"
|
||||
" last value!"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning(
|
||||
"LocalPoolVector: Invalid index. Setting or returning"
|
||||
" last value!\n");
|
||||
#endif
|
||||
return value[vectorSize - 1];
|
||||
}
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline const T& LocalPoolVector<T, vectorSize>::operator[](size_t i) const {
|
||||
if (i < vectorSize) {
|
||||
return value[i];
|
||||
}
|
||||
// If this happens, I have to set some value. I consider this
|
||||
// a configuration error, but I wont exit here.
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::warning << "LocalPoolVector: Invalid index. Setting or returning"
|
||||
" last value!"
|
||||
<< std::endl;
|
||||
#else
|
||||
sif::printWarning(
|
||||
"LocalPoolVector: Invalid index. Setting or returning"
|
||||
" last value!\n");
|
||||
#endif
|
||||
return value[vectorSize - 1];
|
||||
}
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline ReturnValue_t LocalPoolVector<T, vectorSize>::serialize(
|
||||
uint8_t** buffer, size_t* size, size_t maxSize,
|
||||
SerializeIF::Endianness streamEndianness) const {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
for (uint16_t i = 0; i < vectorSize; i++) {
|
||||
result = SerializeAdapter::serialize(&(value[i]), buffer, size, maxSize, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline size_t LocalPoolVector<T, vectorSize>::getSerializedSize() const {
|
||||
return vectorSize * SerializeAdapter::getSerializedSize(value);
|
||||
}
|
||||
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline ReturnValue_t LocalPoolVector<T, vectorSize>::deSerialize(
|
||||
const uint8_t** buffer, size_t* size, SerializeIF::Endianness streamEndianness) {
|
||||
ReturnValue_t result = returnvalue::FAILED;
|
||||
for (uint16_t i = 0; i < vectorSize; i++) {
|
||||
result = SerializeAdapter::deSerialize(&(value[i]), buffer, size, streamEndianness);
|
||||
if (result != returnvalue::OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
template <typename T, uint16_t vectorSize>
|
||||
inline std::ostream& operator<<(std::ostream& out, const LocalPoolVector<T, vectorSize>& var) {
|
||||
out << "Vector: [";
|
||||
for (int i = 0; i < vectorSize; i++) {
|
||||
out << var.value[i];
|
||||
if (i < vectorSize - 1) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << "]";
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ */
|
||||
@@ -1,152 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_
|
||||
#define FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_
|
||||
|
||||
#include "fsfw/housekeeping/AcceptsHkPacketsIF.h"
|
||||
#include "fsfw/ipc/MessageQueueIF.h"
|
||||
#include "fsfw/ipc/messageQueueDefinitions.h"
|
||||
#include "fsfw/returnvalues/returnvalue.h"
|
||||
#include "localPoolDefinitions.h"
|
||||
|
||||
namespace subdp {
|
||||
|
||||
struct ParamsBase {
|
||||
ParamsBase(sid_t sid, bool enableReporting, float collectionInterval, bool diagnostics)
|
||||
: sid(sid),
|
||||
enableReporting(enableReporting),
|
||||
collectionInterval(collectionInterval),
|
||||
diagnostics(diagnostics) {}
|
||||
|
||||
[[nodiscard]] bool isDiagnostics() const { return diagnostics; }
|
||||
|
||||
sid_t sid;
|
||||
bool enableReporting;
|
||||
float collectionInterval;
|
||||
MessageQueueId_t receiver = MessageQueueIF::NO_QUEUE;
|
||||
|
||||
protected:
|
||||
bool diagnostics;
|
||||
};
|
||||
|
||||
struct RegularHkPeriodicParams : public ParamsBase {
|
||||
RegularHkPeriodicParams(sid_t sid, bool enableReporting, float collectionInterval)
|
||||
: ParamsBase(sid, enableReporting, collectionInterval, false) {}
|
||||
};
|
||||
|
||||
struct DiagnosticsHkPeriodicParams : public ParamsBase {
|
||||
DiagnosticsHkPeriodicParams(sid_t sid, bool enableReporting, float collectionInterval)
|
||||
: ParamsBase(sid, enableReporting, collectionInterval, true) {}
|
||||
};
|
||||
|
||||
struct RegularHkUpdateParams : public ParamsBase {
|
||||
RegularHkUpdateParams(sid_t sid, bool enableReporting)
|
||||
: ParamsBase(sid, enableReporting, 0.0, false) {}
|
||||
};
|
||||
|
||||
struct DiagnosticsHkUpdateParams : public ParamsBase {
|
||||
DiagnosticsHkUpdateParams(sid_t sid, bool enableReporting)
|
||||
: ParamsBase(sid, enableReporting, 0.0, true) {}
|
||||
};
|
||||
} // namespace subdp
|
||||
|
||||
class ProvidesDataPoolSubscriptionIF {
|
||||
public:
|
||||
virtual ~ProvidesDataPoolSubscriptionIF() = default;
|
||||
/**
|
||||
* @brief Subscribe for the generation of periodic packets. Used for regular HK packets
|
||||
* @details
|
||||
* This subscription mechanism will generally be used by the data creator
|
||||
* to generate housekeeping packets which are downlinked directly.
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t subscribeForRegularPeriodicPacket(
|
||||
subdp::RegularHkPeriodicParams params) = 0;
|
||||
/**
|
||||
* @brief Subscribe for the generation of periodic packets. Used for diagnostic packets
|
||||
* @details
|
||||
* This subscription mechanism will generally be used by the data creator
|
||||
* to generate housekeeping packets which are downlinked directly.
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t subscribeForDiagPeriodicPacket(
|
||||
subdp::DiagnosticsHkPeriodicParams params) = 0;
|
||||
|
||||
[[deprecated(
|
||||
"Please use the new API which takes all arguments as one wrapper "
|
||||
"struct")]] virtual ReturnValue_t
|
||||
subscribeForPeriodicPacket(sid_t sid, bool enableReporting, float collectionInterval,
|
||||
bool isDiagnostics,
|
||||
object_id_t packetDestination = objects::NO_OBJECT) {
|
||||
if (isDiagnostics) {
|
||||
subdp::DiagnosticsHkPeriodicParams params(sid, enableReporting, collectionInterval);
|
||||
return subscribeForDiagPeriodicPacket(params);
|
||||
} else {
|
||||
subdp::RegularHkPeriodicParams params(sid, enableReporting, collectionInterval);
|
||||
return subscribeForRegularPeriodicPacket(params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe for the generation of packets if the dataset
|
||||
* is marked as changed.
|
||||
* @details
|
||||
* This subscription mechanism will generally be used by the data creator.
|
||||
* @param sid
|
||||
* @param isDiagnostics
|
||||
* @param packetDestination
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t subscribeForRegularUpdatePacket(subdp::RegularHkUpdateParams params) = 0;
|
||||
virtual ReturnValue_t subscribeForDiagUpdatePacket(subdp::DiagnosticsHkUpdateParams params) = 0;
|
||||
|
||||
[[deprecated(
|
||||
"Please use the new API which takes all arguments as one wrapper "
|
||||
"struct")]] virtual ReturnValue_t
|
||||
subscribeForUpdatePacket(sid_t sid, bool reportingEnabled, bool isDiagnostics,
|
||||
object_id_t packetDestination = objects::NO_OBJECT) {
|
||||
if (isDiagnostics) {
|
||||
subdp::DiagnosticsHkUpdateParams params(sid, reportingEnabled);
|
||||
return subscribeForDiagUpdatePacket(params);
|
||||
} else {
|
||||
subdp::RegularHkUpdateParams params(sid, reportingEnabled);
|
||||
return subscribeForRegularUpdatePacket(params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe for a notification message which will be sent
|
||||
* if a dataset has changed.
|
||||
* @details
|
||||
* This subscription mechanism will generally be used internally by
|
||||
* other software components.
|
||||
* @param setId Set ID of the set to receive update messages from.
|
||||
* @param destinationObject Object ID of the receiver.
|
||||
* @param targetQueueId Receiver queue ID
|
||||
* @param generateSnapshot If this is set to true, a copy of the current data with a
|
||||
* timestamp will be generated and sent via message.
|
||||
* Otherwise, only an notification message is sent.
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t subscribeForSetUpdateMessage(uint32_t setId, object_id_t destinationObject,
|
||||
MessageQueueId_t targetQueueId,
|
||||
bool generateSnapshot) = 0;
|
||||
/**
|
||||
* @brief Subscribe for an notification message which will be sent if a
|
||||
* pool variable has changed.
|
||||
* @details
|
||||
* This subscription mechanism will generally be used internally by
|
||||
* other software components.
|
||||
* @param localPoolId Pool ID of the pool variable
|
||||
* @param destinationObject Object ID of the receiver
|
||||
* @param targetQueueId Receiver queue ID
|
||||
* @param generateSnapshot If this is set to true, a copy of the current data with a
|
||||
* timestamp will be generated and sent via message. Otherwise,
|
||||
* only an notification message is sent.
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t subscribeForVariableUpdateMessage(lp_id_t localPoolId,
|
||||
object_id_t destinationObject,
|
||||
MessageQueueId_t targetQueueId,
|
||||
bool generateSnapshot) = 0;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_ */
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_
|
||||
#define FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../datapool/SharedDataSetIF.h"
|
||||
#include "../objectmanager/SystemObject.h"
|
||||
#include "LocalPoolDataSetBase.h"
|
||||
|
||||
/**
|
||||
* This local dataset variation can be used if the dataset is used concurrently across
|
||||
* multiple threads. It provides a lock in addition to all other functionalities provided
|
||||
* by the LocalPoolDataSetBase class.
|
||||
*
|
||||
* The user is completely responsible for lockingand unlocking the dataset when using the
|
||||
* shared dataset.
|
||||
*/
|
||||
class SharedLocalDataSet : public SystemObject,
|
||||
public LocalPoolDataSetBase,
|
||||
public SharedDataSetIF {
|
||||
public:
|
||||
SharedLocalDataSet(object_id_t objectId, HasLocalDataPoolIF* owner, uint32_t setId,
|
||||
const size_t maxSize);
|
||||
SharedLocalDataSet(object_id_t objectId, sid_t sid, const size_t maxSize);
|
||||
|
||||
virtual ~SharedLocalDataSet();
|
||||
|
||||
ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||
dur_millis_t mutexTimeout = 20) override;
|
||||
ReturnValue_t unlockDataset() override;
|
||||
|
||||
private:
|
||||
MutexIF* datasetLock = nullptr;
|
||||
std::vector<PoolVariableIF*> poolVarVector;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_ */
|
||||
@@ -1,2 +0,0 @@
|
||||
target_sources(${LIB_FSFW_NAME} PRIVATE HasLocalDpIFUserAttorney.cpp
|
||||
HasLocalDpIFManagerAttorney.cpp)
|
||||
@@ -1,19 +0,0 @@
|
||||
#include "HasLocalDpIFManagerAttorney.h"
|
||||
|
||||
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
|
||||
#include "fsfw/datapoollocal/LocalPoolDataSetBase.h"
|
||||
#include "fsfw/datapoollocal/LocalPoolObjectBase.h"
|
||||
|
||||
LocalPoolDataSetBase* HasLocalDpIFManagerAttorney::getDataSetHandle(HasLocalDataPoolIF* clientIF,
|
||||
sid_t sid) {
|
||||
return clientIF->getDataSetHandle(sid);
|
||||
}
|
||||
|
||||
LocalPoolObjectBase* HasLocalDpIFManagerAttorney::getPoolObjectHandle(HasLocalDataPoolIF* clientIF,
|
||||
lp_id_t localPoolId) {
|
||||
return clientIF->getPoolObjectHandle(localPoolId);
|
||||
}
|
||||
|
||||
object_id_t HasLocalDpIFManagerAttorney::getObjectId(HasLocalDataPoolIF* clientIF) {
|
||||
return clientIF->getObjectId();
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_
|
||||
#define FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_
|
||||
|
||||
#include "fsfw/datapoollocal/localPoolDefinitions.h"
|
||||
|
||||
class HasLocalDataPoolIF;
|
||||
class LocalPoolDataSetBase;
|
||||
class LocalPoolObjectBase;
|
||||
|
||||
class HasLocalDpIFManagerAttorney {
|
||||
static LocalPoolDataSetBase* getDataSetHandle(HasLocalDataPoolIF* clientIF, sid_t sid);
|
||||
|
||||
static LocalPoolObjectBase* getPoolObjectHandle(HasLocalDataPoolIF* clientIF,
|
||||
lp_id_t localPoolId);
|
||||
|
||||
static object_id_t getObjectId(HasLocalDataPoolIF* clientIF);
|
||||
|
||||
friend class LocalDataPoolManager;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_ */
|
||||
@@ -1,8 +0,0 @@
|
||||
#include "HasLocalDpIFUserAttorney.h"
|
||||
|
||||
#include "fsfw/datapoollocal/AccessLocalPoolF.h"
|
||||
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
|
||||
|
||||
AccessPoolManagerIF* HasLocalDpIFUserAttorney::getAccessorHandle(HasLocalDataPoolIF* clientIF) {
|
||||
return clientIF->getAccessorHandle();
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_
|
||||
#define FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_
|
||||
|
||||
#include "fsfw/datapoollocal/LocalPoolDataSetBase.h"
|
||||
|
||||
class LocalPoolDataSetAttorney {
|
||||
private:
|
||||
static void initializePeriodicHelper(LocalPoolDataSetBase& set, float collectionInterval,
|
||||
uint32_t minimumPeriodicIntervalMs) {
|
||||
set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs);
|
||||
}
|
||||
|
||||
static void setReportingEnabled(LocalPoolDataSetBase& set, bool enabled) {
|
||||
set.setReportingEnabled(enabled);
|
||||
}
|
||||
|
||||
static bool getReportingEnabled(LocalPoolDataSetBase& set) { return set.getReportingEnabled(); }
|
||||
|
||||
static PeriodicHousekeepingHelper* getPeriodicHelper(LocalPoolDataSetBase& set) {
|
||||
return set.periodicHelper;
|
||||
}
|
||||
|
||||
friend class LocalDataPoolManager;
|
||||
};
|
||||
|
||||
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_ */
|
||||
@@ -71,7 +71,8 @@ bool AssemblyBase::handleChildrenChangedHealth() {
|
||||
if (iter == childrenMap.end()) {
|
||||
return false;
|
||||
}
|
||||
HealthState healthState = healthHelper.healthTable->getHealth(iter->first);
|
||||
HealthState healthState =
|
||||
healthHelper.healthTable->getHealth(convertToDeviceObjectId(iter->first));
|
||||
if (healthState == HasHealthIF::NEEDS_RECOVERY) {
|
||||
triggerEvent(TRYING_RECOVERY, iter->first, 0);
|
||||
recoveryState = RECOVERY_STARTED;
|
||||
@@ -91,10 +92,14 @@ bool AssemblyBase::handleChildrenChangedHealth() {
|
||||
void AssemblyBase::handleChildrenTransition() {
|
||||
if (commandsOutstanding <= 0) {
|
||||
switch (internalState) {
|
||||
case STATE_NEED_SECOND_STEP:
|
||||
case STATE_NEED_SECOND_STEP: {
|
||||
internalState = STATE_SECOND_STEP;
|
||||
commandChildren(targetMode, targetSubmode);
|
||||
ReturnValue_t result = commandChildren(targetMode, targetSubmode);
|
||||
if (result == NEED_SECOND_STEP) {
|
||||
internalState = STATE_NEED_SECOND_STEP;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case STATE_OVERWRITE_HEALTH: {
|
||||
internalState = STATE_SINGLE_STEP;
|
||||
ReturnValue_t result = commandChildren(mode, submode);
|
||||
@@ -170,7 +175,7 @@ ReturnValue_t AssemblyBase::checkChildrenStateOff() {
|
||||
|
||||
ReturnValue_t AssemblyBase::checkChildOff(uint32_t objectId) {
|
||||
ChildInfo childInfo = childrenMap.find(objectId)->second;
|
||||
if (healthHelper.healthTable->isCommandable(objectId)) {
|
||||
if (healthHelper.healthTable->isCommandable(convertToDeviceObjectId(objectId))) {
|
||||
if (childInfo.submode != SUBMODE_NONE) {
|
||||
return returnvalue::FAILED;
|
||||
} else {
|
||||
@@ -227,7 +232,7 @@ bool AssemblyBase::checkAndHandleRecovery() {
|
||||
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
|
||||
// The timeout can be defined by #setRecoveryWaitTimer
|
||||
recoveryState = RECOVERY_WAIT;
|
||||
recoveryOffTimer.resetTimer();
|
||||
return true;
|
||||
@@ -235,14 +240,14 @@ bool AssemblyBase::checkAndHandleRecovery() {
|
||||
if (recoveryOffTimer.isBusy()) {
|
||||
return true;
|
||||
}
|
||||
triggerEvent(RECOVERY_STEP, 0);
|
||||
triggerEvent(RECOVERY_WAITING, recoveringDevice->first);
|
||||
sendHealthCommand(recoveringDevice->second.commandQueue, HEALTHY);
|
||||
internalState = STATE_NONE;
|
||||
recoveryState = RECOVERY_ONGOING;
|
||||
// Don't check state!
|
||||
return true;
|
||||
case RECOVERY_ONGOING:
|
||||
triggerEvent(RECOVERY_STEP, 1);
|
||||
triggerEvent(RECOVERY_RESTARTING, recoveringDevice->first);
|
||||
recoveryState = RECOVERY_ONGOING_2;
|
||||
recoveringDevice->second.healthChanged = false;
|
||||
// Device should be healthy again, so restart a transition.
|
||||
@@ -250,7 +255,7 @@ bool AssemblyBase::checkAndHandleRecovery() {
|
||||
doStartTransition(targetMode, targetSubmode);
|
||||
return true;
|
||||
case RECOVERY_ONGOING_2:
|
||||
triggerEvent(RECOVERY_DONE);
|
||||
triggerEvent(RECOVERY_DONE, recoveringDevice->first);
|
||||
// Now we're through, but not sure if it was successful.
|
||||
recoveryState = RECOVERY_IDLE;
|
||||
return false;
|
||||
@@ -264,7 +269,14 @@ void AssemblyBase::overwriteDeviceHealth(object_id_t objectId, HasHealthIF::Heal
|
||||
triggerEvent(OVERWRITING_HEALTH, objectId, oldHealth);
|
||||
internalState = STATE_OVERWRITE_HEALTH;
|
||||
modeHelper.setForced(true);
|
||||
sendHealthCommand(childrenMap[objectId].commandQueue, EXTERNAL_CONTROL);
|
||||
if (childrenMap.find(objectId) != childrenMap.end()) {
|
||||
sendHealthCommand(childrenMap.at(objectId).commandQueue, EXTERNAL_CONTROL);
|
||||
} else {
|
||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||
sif::debug << std::hex << SystemObject::getObjectId() << ": invalid mode table entry"
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyBase::triggerModeHelperEvents(Mode_t mode, Submode_t submode) {
|
||||
@@ -274,3 +286,7 @@ void AssemblyBase::triggerModeHelperEvents(Mode_t mode, Submode_t submode) {
|
||||
triggerEvent(CHANGING_MODE, mode, submode);
|
||||
}
|
||||
}
|
||||
|
||||
void AssemblyBase::setRecoveryWaitTimer(uint32_t timeoutMS) {
|
||||
recoveryOffTimer.setTimeout(timeoutMS);
|
||||
}
|
||||
@@ -206,6 +206,8 @@ class AssemblyBase : public SubsystemBase {
|
||||
void overwriteDeviceHealth(object_id_t objectId, HasHealthIF::HealthState oldHealth);
|
||||
|
||||
void triggerModeHelperEvents(Mode_t mode, Submode_t submode);
|
||||
|
||||
void setRecoveryWaitTimer(uint32_t timeoutMS);
|
||||
};
|
||||
|
||||
#endif /* FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ */
|
||||
@@ -1,11 +1,10 @@
|
||||
#include "DeviceHandlerBase.h"
|
||||
|
||||
#include "fsfw/datapool/PoolReadGuard.h"
|
||||
#include "fsfw/datapoollocal/LocalPoolVariable.h"
|
||||
#include "fsfw/datapool/PoolVariable.h"
|
||||
#include "fsfw/devicehandlers/AcceptsDeviceResponsesIF.h"
|
||||
#include "fsfw/devicehandlers/DeviceTmReportingWrapper.h"
|
||||
#include "fsfw/globalfunctions/CRC.h"
|
||||
#include "fsfw/housekeeping/HousekeepingMessage.h"
|
||||
#include "fsfw/ipc/MessageQueueMessage.h"
|
||||
#include "fsfw/ipc/QueueFactory.h"
|
||||
#include "fsfw/objectmanager/ObjectManager.h"
|
||||
@@ -28,11 +27,12 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, object_id_t device
|
||||
storedRawData(StorageManagerIF::INVALID_ADDRESS),
|
||||
deviceCommunicationId(deviceCommunication),
|
||||
comCookie(comCookie),
|
||||
sharedPool(DeviceHandlerBase::getObjectId()),
|
||||
healthHelper(this, setObjectId),
|
||||
modeHelper(this),
|
||||
parameterHelper(this),
|
||||
actionHelper(this, nullptr),
|
||||
poolManager(this, nullptr),
|
||||
hkHelper(this, nullptr),
|
||||
childTransitionFailure(returnvalue::OK),
|
||||
fdirInstance(fdirInstance),
|
||||
defaultFDIRUsed(fdirInstance == nullptr),
|
||||
@@ -111,7 +111,7 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) {
|
||||
doGetRead();
|
||||
/* This will be performed after datasets have been updated by the
|
||||
custom device implementation. */
|
||||
poolManager.performHkOperation();
|
||||
hkHelper.performHkOperation();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -152,8 +152,7 @@ ReturnValue_t DeviceHandlerBase::initialize() {
|
||||
}
|
||||
|
||||
if (rawDataReceiverId != objects::NO_OBJECT) {
|
||||
AcceptsDeviceResponsesIF* rawReceiver =
|
||||
ObjectManager::instance()->get<AcceptsDeviceResponsesIF>(rawDataReceiverId);
|
||||
auto* rawReceiver = ObjectManager::instance()->get<AcceptsDeviceResponsesIF>(rawDataReceiverId);
|
||||
|
||||
if (rawReceiver == nullptr) {
|
||||
printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize",
|
||||
@@ -214,7 +213,7 @@ ReturnValue_t DeviceHandlerBase::initialize() {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = poolManager.initialize(commandQueue);
|
||||
result = hkHelper.initialize(commandQueue);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
@@ -226,7 +225,7 @@ ReturnValue_t DeviceHandlerBase::initialize() {
|
||||
// Set temperature target state to NON_OP.
|
||||
if (pg.getReadResult() == returnvalue::OK) {
|
||||
thermalSet->heaterRequest.value = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL;
|
||||
thermalSet->heaterRequest.setValid(true);
|
||||
// thermalSet->heaterRequest.setValid(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +287,7 @@ void DeviceHandlerBase::readCommandQueue() {
|
||||
return;
|
||||
}
|
||||
|
||||
result = poolManager.handleHousekeepingMessage(&command);
|
||||
result = hkHelper.handleHousekeepingMessage(&command);
|
||||
if (result == returnvalue::OK) {
|
||||
return;
|
||||
}
|
||||
@@ -413,7 +412,7 @@ ReturnValue_t DeviceHandlerBase::isModeCombinationValid(Mode_t mode, Submode_t s
|
||||
}
|
||||
|
||||
ReturnValue_t DeviceHandlerBase::insertInCommandAndReplyMap(
|
||||
DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles, LocalPoolDataSetBase* replyDataSet,
|
||||
DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles, dp::SharedSetBase* replyDataSet,
|
||||
size_t replyLen, bool periodic, bool hasDifferentReplyId, DeviceCommandId_t replyId,
|
||||
Countdown* countdown) {
|
||||
// No need to check, as we may try to insert multiple times.
|
||||
@@ -428,7 +427,7 @@ ReturnValue_t DeviceHandlerBase::insertInCommandAndReplyMap(
|
||||
|
||||
ReturnValue_t DeviceHandlerBase::insertInReplyMap(DeviceCommandId_t replyId,
|
||||
uint16_t maxDelayCycles,
|
||||
LocalPoolDataSetBase* dataSet, size_t replyLen,
|
||||
dp::SharedSetBase* dataSet, size_t replyLen,
|
||||
bool periodic, Countdown* countdown) {
|
||||
DeviceReplyInfo info;
|
||||
info.maxDelayCycles = maxDelayCycles;
|
||||
@@ -529,7 +528,7 @@ ReturnValue_t DeviceHandlerBase::updatePeriodicReply(bool enable, DeviceCommandI
|
||||
}
|
||||
|
||||
ReturnValue_t DeviceHandlerBase::setReplyDataset(DeviceCommandId_t replyId,
|
||||
LocalPoolDataSetBase* dataSet) {
|
||||
dp::SharedSetBase* dataSet) {
|
||||
auto replyIter = deviceReplyMap.find(replyId);
|
||||
if (replyIter == deviceReplyMap.end()) {
|
||||
return returnvalue::FAILED;
|
||||
@@ -593,7 +592,7 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) {
|
||||
if (thermalSet->heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) {
|
||||
thermalSet->heaterRequest.value = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL;
|
||||
}
|
||||
thermalSet->heaterRequest.setValid(true);
|
||||
// thermalSet->heaterRequest.setValid(true);
|
||||
}
|
||||
}
|
||||
/* TODO: This will probably be done by the LocalDataPoolManager now */
|
||||
@@ -1269,7 +1268,7 @@ ReturnValue_t DeviceHandlerBase::letChildHandleMessage(CommandMessage* message)
|
||||
|
||||
void DeviceHandlerBase::handleDeviceTm(const uint8_t* rawData, size_t rawDataLen,
|
||||
DeviceCommandId_t replyId, bool forceDirectTm) {
|
||||
SerialBufferAdapter bufferWrapper(rawData, rawDataLen);
|
||||
SerialBufferAdapter<uint32_t> bufferWrapper(rawData, rawDataLen);
|
||||
handleDeviceTm(bufferWrapper, replyId, forceDirectTm);
|
||||
}
|
||||
|
||||
@@ -1468,8 +1467,9 @@ Submode_t DeviceHandlerBase::getInitialSubmode() { return SUBMODE_NONE; }
|
||||
|
||||
void DeviceHandlerBase::performOperationHook() {}
|
||||
|
||||
/*
|
||||
ReturnValue_t DeviceHandlerBase::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) {
|
||||
PeriodicHkGenerationHelper& poolManager) {
|
||||
if (thermalStateCfg.has_value()) {
|
||||
localDataPoolMap.emplace(thermalStateCfg.value().thermalStatePoolId,
|
||||
new PoolEntry<DeviceHandlerIF::dh_thermal_state_t>());
|
||||
@@ -1478,6 +1478,7 @@ ReturnValue_t DeviceHandlerBase::initializeLocalDataPool(localpool::DataPool& lo
|
||||
}
|
||||
return returnvalue::OK;
|
||||
}
|
||||
*/
|
||||
|
||||
ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() {
|
||||
// In this function, the task handle should be valid if the task
|
||||
@@ -1485,7 +1486,7 @@ ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() {
|
||||
if (executingTask != nullptr) {
|
||||
pstIntervalMs = executingTask->getPeriodMs();
|
||||
}
|
||||
this->poolManager.initializeAfterTaskCreation();
|
||||
// this->poolManager.initializeAfterTaskCreation();
|
||||
|
||||
if (thermalStateCfg.has_value()) {
|
||||
ThermalStateCfg& cfg = thermalStateCfg.value();
|
||||
@@ -1497,20 +1498,11 @@ ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() {
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
LocalPoolDataSetBase* DeviceHandlerBase::getDataSetHandle(sid_t sid) {
|
||||
auto iter = deviceReplyMap.find(sid.ownerSetId);
|
||||
if (iter != deviceReplyMap.end()) {
|
||||
return iter->second.dataSet;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
object_id_t DeviceHandlerBase::getObjectId() const { return SystemObject::getObjectId(); }
|
||||
|
||||
void DeviceHandlerBase::setStartUpImmediately() { this->setStartupImmediately = true; }
|
||||
|
||||
dur_millis_t DeviceHandlerBase::getPeriodicOperationFrequency() const { return pstIntervalMs; }
|
||||
// dur_millis_t DeviceHandlerBase::getPeriodicOperationFrequency() const { return pstIntervalMs; }
|
||||
|
||||
DeviceCommandId_t DeviceHandlerBase::getPendingCommand() const {
|
||||
if (cookieInfo.pendingCommand != deviceCommandMap.end()) {
|
||||
@@ -1519,17 +1511,6 @@ DeviceCommandId_t DeviceHandlerBase::getPendingCommand() const {
|
||||
return DeviceHandlerIF::NO_COMMAND_ID;
|
||||
}
|
||||
|
||||
void DeviceHandlerBase::setNormalDatapoolEntriesInvalid() {
|
||||
for (const auto& reply : deviceReplyMap) {
|
||||
if (reply.second.dataSet != nullptr) {
|
||||
PoolReadGuard pg(reply.second.dataSet);
|
||||
if (pg.getReadResult() == returnvalue::OK) {
|
||||
reply.second.dataSet->setValidity(false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType, const char* functionName,
|
||||
ReturnValue_t errorCode, const char* errorPrint) {
|
||||
if (errorPrint == nullptr) {
|
||||
@@ -1570,8 +1551,6 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType, const ch
|
||||
}
|
||||
}
|
||||
|
||||
LocalDataPoolManager* DeviceHandlerBase::getHkManagerHandle() { return &poolManager; }
|
||||
|
||||
MessageQueueId_t DeviceHandlerBase::getCommanderQueueId(DeviceCommandId_t replyId) const {
|
||||
auto commandIter = deviceCommandMap.find(replyId);
|
||||
if (commandIter == deviceCommandMap.end()) {
|
||||
@@ -1628,3 +1607,7 @@ ReturnValue_t DeviceHandlerBase::finishAction(bool success, DeviceCommandId_t ac
|
||||
actionHelper.finish(success, commandIter->second.sendReplyTo, action, result);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
void DeviceHandlerBase::setNormalDatapoolEntriesInvalid() { return; }
|
||||
|
||||
hk::PeriodicHelper& DeviceHandlerBase::getHkHelper() { return hkHelper; }
|
||||
@@ -1,5 +1,6 @@
|
||||
#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_
|
||||
#define FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/housekeeping/PeriodicHkHelper.h>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -10,9 +11,6 @@
|
||||
#include "DeviceHandlerThermalSet.h"
|
||||
#include "fsfw/action/ActionHelper.h"
|
||||
#include "fsfw/action/HasActionsIF.h"
|
||||
#include "fsfw/datapool/PoolVariableIF.h"
|
||||
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
|
||||
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
|
||||
#include "fsfw/health/HealthHelper.h"
|
||||
#include "fsfw/ipc/MessageQueueIF.h"
|
||||
#include "fsfw/modes/HasModesIF.h"
|
||||
@@ -20,7 +18,6 @@
|
||||
#include "fsfw/parameters/ParameterHelper.h"
|
||||
#include "fsfw/power/PowerSwitchIF.h"
|
||||
#include "fsfw/returnvalues/returnvalue.h"
|
||||
#include "fsfw/serviceinterface/ServiceInterface.h"
|
||||
#include "fsfw/serviceinterface/serviceInterfaceDefintions.h"
|
||||
#include "fsfw/subsystem/ModeTreeConnectionIF.h"
|
||||
#include "fsfw/tasks/ExecutableObjectIF.h"
|
||||
@@ -88,7 +85,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
public ModeTreeChildIF,
|
||||
public ModeTreeConnectionIF,
|
||||
public ReceivesParameterMessagesIF,
|
||||
public HasLocalDataPoolIF {
|
||||
public hk::GeneratesPeriodicHkIF {
|
||||
friend void(Factory::setStaticFrameworkObjectIds)();
|
||||
|
||||
public:
|
||||
@@ -96,7 +93,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* The constructor passes the objectId to the SystemObject().
|
||||
*
|
||||
* @param setObjectId the ObjectId to pass to the SystemObject() Constructor
|
||||
* @param deviceCommuncation Communcation Interface object which is used
|
||||
* @param deviceCommunication Communication Interface object which is used
|
||||
* to implement communication functions
|
||||
* @param comCookie This object will be passed to the communication inter-
|
||||
* face and can contain user-defined information about the communication.
|
||||
@@ -109,6 +106,8 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
void setCustomFdir(FailureIsolationBase *fdir);
|
||||
void setPowerSwitcher(PowerSwitchIF *switcher);
|
||||
|
||||
hk::PeriodicHelper &getHkHelper();
|
||||
|
||||
/**
|
||||
* extending the modes of DeviceHandler IF for internal state machine
|
||||
*/
|
||||
@@ -230,7 +229,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
ReturnValue_t initializeAfterTaskCreation() override;
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~DeviceHandlerBase();
|
||||
~DeviceHandlerBase() override;
|
||||
|
||||
/**
|
||||
* Implementation of ExecutableObjectIF function
|
||||
@@ -238,7 +237,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* @param task_ Pointer to the taskIF of this task
|
||||
*/
|
||||
virtual void setTaskIF(PeriodicTaskIF *task_) override;
|
||||
virtual MessageQueueId_t getCommandQueue(void) const override;
|
||||
virtual MessageQueueId_t getCommandQueue() const override;
|
||||
|
||||
/** Explicit interface implementation of getObjectId */
|
||||
virtual object_id_t getObjectId() const override;
|
||||
@@ -506,7 +505,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* - @c returnvalue::FAILED else.
|
||||
*/
|
||||
ReturnValue_t insertInCommandAndReplyMap(DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles,
|
||||
LocalPoolDataSetBase *replyDataSet = nullptr,
|
||||
dp::SharedSetBase *replyDataSet = nullptr,
|
||||
size_t replyLen = 0, bool periodic = false,
|
||||
bool hasDifferentReplyId = false,
|
||||
DeviceCommandId_t replyId = 0,
|
||||
@@ -527,7 +526,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* - @c returnvalue::FAILED else.
|
||||
*/
|
||||
ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles,
|
||||
LocalPoolDataSetBase *dataSet = nullptr, size_t replyLen = 0,
|
||||
dp::SharedSetBase *dataSet = nullptr, size_t replyLen = 0,
|
||||
bool periodic = false, Countdown *countdown = nullptr);
|
||||
|
||||
/**
|
||||
@@ -580,7 +579,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* @details
|
||||
* Used by the local data pool manager.
|
||||
*/
|
||||
ReturnValue_t setReplyDataset(DeviceCommandId_t replyId, LocalPoolDataSetBase *dataset);
|
||||
ReturnValue_t setReplyDataset(DeviceCommandId_t replyId, dp::SharedSetBase *dataset);
|
||||
|
||||
/**
|
||||
* Get the time needed to transit from modeFrom to modeTo.
|
||||
@@ -607,8 +606,8 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* @param localDataPoolMap
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
LocalDataPoolManager &poolManager) override;
|
||||
// virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
|
||||
// PeriodicHkGenerationHelper &poolManager) override;
|
||||
/**
|
||||
* @brief Set all datapool variables that are update periodically in
|
||||
* normal mode invalid
|
||||
@@ -619,15 +618,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* method optionally.
|
||||
*/
|
||||
virtual void setNormalDatapoolEntriesInvalid();
|
||||
/**
|
||||
* @brief Get the dataset handle for a given SID.
|
||||
* @details
|
||||
* The default implementation will use the deviceCommandMap to look for the corresponding
|
||||
* dataset handle. The user can override this function if this is not desired.
|
||||
* @param sid
|
||||
* @return
|
||||
*/
|
||||
virtual LocalPoolDataSetBase *getDataSetHandle(sid_t sid) override;
|
||||
|
||||
/* HasModesIF overrides */
|
||||
virtual void startTransition(Mode_t mode, Submode_t submode) override;
|
||||
@@ -797,6 +787,8 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
/** Cookie used for communication */
|
||||
CookieIF *comCookie;
|
||||
|
||||
dp::SharedPool sharedPool;
|
||||
|
||||
/* Health helper for HasHealthIF */
|
||||
HealthHelper healthHelper;
|
||||
/* Mode helper for HasModesIF */
|
||||
@@ -806,7 +798,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
/* Action helper for HasActionsIF */
|
||||
ActionHelper actionHelper;
|
||||
/* Housekeeping Manager */
|
||||
LocalDataPoolManager poolManager;
|
||||
hk::PeriodicHelper hkHelper;
|
||||
|
||||
/**
|
||||
* @brief Information about commands
|
||||
@@ -854,7 +846,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
//! The dataset used to access housekeeping data related to the
|
||||
//! respective device reply. Will point to a dataset held by
|
||||
//! the child handler (if one is specified)
|
||||
LocalPoolDataSetBase *dataSet = nullptr;
|
||||
dp::SharedSetBase *dataSet = nullptr;
|
||||
//! The command that expects this reply.
|
||||
DeviceCommandMap::iterator command;
|
||||
//! Instead of using delayCycles to specify the maximum time to wait for the device reply, it
|
||||
@@ -1006,12 +998,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
*/
|
||||
virtual void doOnActivity();
|
||||
|
||||
/**
|
||||
* Required for HasLocalDataPoolIF, return a handle to the local pool manager.
|
||||
* @return
|
||||
*/
|
||||
LocalDataPoolManager *getHkManagerHandle() override;
|
||||
|
||||
const HasHealthIF *getOptHealthIF() const override;
|
||||
const HasModesIF &getModeIF() const override;
|
||||
|
||||
@@ -1403,8 +1389,6 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
|
||||
ReturnValue_t handleDeviceHandlerMessage(CommandMessage *message);
|
||||
|
||||
virtual dur_millis_t getPeriodicOperationFrequency() const override;
|
||||
|
||||
void parseReply(const uint8_t *receivedData, size_t receivedDataLen);
|
||||
|
||||
void handleTransitionToOnMode(Mode_t commandedMode, Submode_t commandedSubmode);
|
||||
@@ -1424,6 +1408,4 @@ class DeviceHandlerBase : public DeviceHandlerIF,
|
||||
* @brief Disables all commands and replies when device is set to MODE_OFF
|
||||
*/
|
||||
void disableCommandsAndReplies();
|
||||
};
|
||||
|
||||
#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */
|
||||
};
|
||||
@@ -2,11 +2,11 @@
|
||||
#define FSFW_DEVICEHANDLERS_DEVICEHANDLERIF_H_
|
||||
|
||||
#include "../action/HasActionsIF.h"
|
||||
#include "../datapoollocal/localPoolDefinitions.h"
|
||||
#include "../events/Event.h"
|
||||
#include "../ipc/MessageQueueSenderIF.h"
|
||||
#include "../modes/HasModesIF.h"
|
||||
#include "DeviceHandlerMessage.h"
|
||||
#include "fsfw/datapool/definitions.h"
|
||||
|
||||
/**
|
||||
* This is used to uniquely identify commands that are sent to a device
|
||||
@@ -120,10 +120,10 @@ class DeviceHandlerIF {
|
||||
NOTHING //!< Do nothing.
|
||||
};
|
||||
|
||||
static constexpr uint32_t DEFAULT_THERMAL_SET_ID = sid_t::INVALID_SET_ID - 1;
|
||||
static constexpr uint32_t DEFAULT_THERMAL_SET_ID = dp::sid_t::INVALID_SET_ID - 1;
|
||||
|
||||
static constexpr lp_id_t DEFAULT_THERMAL_STATE_POOL_ID = localpool::INVALID_LPID - 2;
|
||||
static constexpr lp_id_t DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID = localpool::INVALID_LPID - 1;
|
||||
static constexpr dp::id_t DEFAULT_THERMAL_STATE_POOL_ID = dp::INVALID_LPID - 2;
|
||||
static constexpr dp::id_t DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID = dp::INVALID_LPID - 1;
|
||||
|
||||
/**
|
||||
* Default Destructor
|
||||
@@ -138,8 +138,8 @@ class DeviceHandlerIF {
|
||||
};
|
||||
|
||||
struct ThermalStateCfg {
|
||||
lp_id_t thermalStatePoolId = DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID;
|
||||
lp_id_t thermalRequestPoolId = DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID;
|
||||
dp::id_t thermalStatePoolId = DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID;
|
||||
dp::id_t thermalRequestPoolId = DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID;
|
||||
uint32_t thermalSetId = DeviceHandlerIF::DEFAULT_THERMAL_SET_ID;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_
|
||||
#define FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_
|
||||
|
||||
#include "../datapoollocal/LocalPoolVariable.h"
|
||||
#include "../datapoollocal/StaticLocalDataSet.h"
|
||||
#include "DeviceHandlerIF.h"
|
||||
#include <fsfw/datapool/StaticSharedSet.h>
|
||||
#include <fsfw/housekeeping/GeneratesPeriodicHkIF.h>
|
||||
|
||||
class DeviceHandlerThermalSet : public StaticLocalDataSet<2> {
|
||||
#include "DeviceHandlerIF.h"
|
||||
#include "fsfw/datapool/PoolVariable.h"
|
||||
|
||||
class DeviceHandlerThermalSet : public dp::StaticSharedSet<2> {
|
||||
public:
|
||||
DeviceHandlerThermalSet(HasLocalDataPoolIF* hkOwner, ThermalStateCfg cfg)
|
||||
DeviceHandlerThermalSet(hk::GeneratesPeriodicHkIF* hkOwner, ThermalStateCfg cfg)
|
||||
: DeviceHandlerThermalSet(hkOwner->getObjectId(), cfg) {}
|
||||
|
||||
DeviceHandlerThermalSet(object_id_t deviceHandler, ThermalStateCfg cfg)
|
||||
: StaticLocalDataSet(sid_t(deviceHandler, cfg.thermalSetId)),
|
||||
: StaticSharedSet(dp::structure_id_t(deviceHandler, cfg.thermalSetId), true),
|
||||
thermalStatePoolId(cfg.thermalStatePoolId),
|
||||
heaterRequestPoolId(cfg.thermalRequestPoolId) {}
|
||||
|
||||
const lp_id_t thermalStatePoolId;
|
||||
const lp_id_t heaterRequestPoolId;
|
||||
const dp::id_t thermalStatePoolId;
|
||||
const dp::id_t heaterRequestPoolId;
|
||||
|
||||
lp_var_t<DeviceHandlerIF::dh_thermal_state_t> thermalState =
|
||||
lp_var_t<DeviceHandlerIF::dh_thermal_state_t>(sid.objectId, thermalStatePoolId, this);
|
||||
lp_var_t<DeviceHandlerIF::dh_heater_request_t> heaterRequest =
|
||||
lp_var_t<DeviceHandlerIF::dh_heater_request_t>(sid.objectId, heaterRequestPoolId, this);
|
||||
dp::var_t<DeviceHandlerIF::dh_thermal_state_t> thermalState =
|
||||
dp::var_t<DeviceHandlerIF::dh_thermal_state_t>(sid.objectId, thermalStatePoolId, this);
|
||||
dp::var_t<DeviceHandlerIF::dh_heater_request_t> heaterRequest =
|
||||
dp::var_t<DeviceHandlerIF::dh_heater_request_t>(sid.objectId, heaterRequestPoolId, this);
|
||||
};
|
||||
|
||||
#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_ */
|
||||
@@ -8,12 +8,12 @@ FreshDeviceHandlerBase::FreshDeviceHandlerBase(DhbConfig config)
|
||||
: SystemObject(config.objectId),
|
||||
actionHelper(this, nullptr),
|
||||
modeHelper(this),
|
||||
healthHelper(this, getObjectId()),
|
||||
healthHelper(this, FreshDeviceHandlerBase::getObjectId()),
|
||||
paramHelper(this),
|
||||
poolManager(this, nullptr),
|
||||
hkHelper(this, nullptr),
|
||||
fdirInstance(config.fdirInstance),
|
||||
defaultFdirParent(config.defaultFdirParent) {
|
||||
auto mqArgs = MqArgs(config.objectId, static_cast<void*>(this));
|
||||
auto mqArgs = MqArgs(config.objectId, this);
|
||||
messageQueue = QueueFactory::instance()->createMessageQueue(
|
||||
config.msgQueueDepth, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs);
|
||||
}
|
||||
@@ -33,7 +33,7 @@ ReturnValue_t FreshDeviceHandlerBase::performOperation(uint8_t opCode) {
|
||||
handleQueue();
|
||||
fdirInstance->checkForFailures();
|
||||
performDeviceOperation(opCode);
|
||||
poolManager.performHkOperation();
|
||||
hkHelper.performHkOperation();
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ ReturnValue_t FreshDeviceHandlerBase::performDeviceOperationPreQueueHandling(uin
|
||||
|
||||
void FreshDeviceHandlerBase::startTransition(Mode_t mode_, Submode_t submode_) {
|
||||
triggerEvent(CHANGING_MODE, mode_, submode_);
|
||||
// Complete mode transition immediately by default.
|
||||
setMode(mode_, submode_);
|
||||
}
|
||||
|
||||
@@ -97,7 +96,7 @@ ReturnValue_t FreshDeviceHandlerBase::handleQueue() {
|
||||
continue;
|
||||
}
|
||||
|
||||
result = poolManager.handleHousekeepingMessage(&command);
|
||||
result = hkHelper.handleHousekeepingMessage(&command);
|
||||
if (result == returnvalue::OK) {
|
||||
continue;
|
||||
}
|
||||
@@ -127,17 +126,6 @@ ReturnValue_t FreshDeviceHandlerBase::connectModeTreeParent(HasModeTreeChildrenI
|
||||
// Executable Overrides.
|
||||
void FreshDeviceHandlerBase::setTaskIF(PeriodicTaskIF* task_) { executingTask = task_; }
|
||||
|
||||
// Pool Manager overrides.
|
||||
LocalDataPoolManager* FreshDeviceHandlerBase::getHkManagerHandle() { return &poolManager; }
|
||||
|
||||
[[nodiscard]] uint32_t FreshDeviceHandlerBase::getPeriodicOperationFrequency() const {
|
||||
return this->executingTask->getPeriodMs();
|
||||
}
|
||||
|
||||
ReturnValue_t FreshDeviceHandlerBase::initializeAfterTaskCreation() {
|
||||
return poolManager.initializeAfterTaskCreation();
|
||||
}
|
||||
|
||||
ReturnValue_t FreshDeviceHandlerBase::setHealth(HasHealthIF::HealthState health) {
|
||||
// Assembly should handle commanding to OFF.
|
||||
healthHelper.setHealth(health);
|
||||
@@ -174,7 +162,7 @@ ReturnValue_t FreshDeviceHandlerBase::initialize() {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = poolManager.initialize(messageQueue);
|
||||
result = hkHelper.initialize(messageQueue);
|
||||
if (result != returnvalue::OK) {
|
||||
return result;
|
||||
}
|
||||
@@ -201,3 +189,5 @@ ReturnValue_t FreshDeviceHandlerBase::getParameter(uint8_t domainId, uint8_t uni
|
||||
}
|
||||
return INVALID_DOMAIN_ID;
|
||||
}
|
||||
|
||||
datapool::SharedPool* FreshDeviceHandlerBase::getOptionalSharedPool() { return nullptr; }
|
||||
@@ -1,12 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <fsfw/housekeeping/PeriodicHkHelper.h>
|
||||
|
||||
#include "fsfw/action.h"
|
||||
#include "fsfw/datapoollocal/HasLocalDataPoolIF.h"
|
||||
#include "fsfw/datapoollocal/LocalDataPoolManager.h"
|
||||
#include "fsfw/devicehandlers/DeviceHandlerIF.h"
|
||||
#include "fsfw/fdir/FailureIsolationBase.h"
|
||||
#include "fsfw/health/HasHealthIF.h"
|
||||
#include "fsfw/health/HealthHelper.h"
|
||||
#include "fsfw/housekeeping/GeneratesPeriodicHkIF.h"
|
||||
#include "fsfw/modes/HasModesIF.h"
|
||||
#include "fsfw/objectmanager.h"
|
||||
#include "fsfw/parameters/ParameterHelper.h"
|
||||
@@ -61,7 +62,7 @@ class FreshDeviceHandlerBase : public SystemObject,
|
||||
public ModeTreeConnectionIF,
|
||||
public HasActionsIF,
|
||||
public ReceivesParameterMessagesIF,
|
||||
public HasLocalDataPoolIF {
|
||||
public hk::GeneratesPeriodicHkIF {
|
||||
public:
|
||||
explicit FreshDeviceHandlerBase(DhbConfig config);
|
||||
~FreshDeviceHandlerBase() override;
|
||||
@@ -96,16 +97,12 @@ class FreshDeviceHandlerBase : public SystemObject,
|
||||
ReturnValue_t connectModeTreeParent(HasModeTreeChildrenIF& parent) override;
|
||||
ModeTreeChildIF& getModeTreeChildIF() override;
|
||||
|
||||
[[nodiscard]] uint32_t getPeriodicOperationFrequency() const override;
|
||||
|
||||
protected:
|
||||
// Pool Manager overrides.
|
||||
LocalDataPoolManager* getHkManagerHandle() override;
|
||||
ActionHelper actionHelper;
|
||||
ModeHelper modeHelper;
|
||||
HealthHelper healthHelper;
|
||||
ParameterHelper paramHelper;
|
||||
LocalDataPoolManager poolManager;
|
||||
hk::PeriodicHelper hkHelper;
|
||||
|
||||
bool hasCustomFdir = false;
|
||||
FailureIsolationBase* fdirInstance;
|
||||
@@ -151,6 +148,24 @@ class FreshDeviceHandlerBase : public SystemObject,
|
||||
// System Object overrides.
|
||||
ReturnValue_t initialize() override;
|
||||
|
||||
/**
|
||||
* This function is implemented to serialize a housekeeping packet when a HK message to
|
||||
* generate the packet is received, or periodic generation is necessary. The user should serialize
|
||||
* the HK set into the provided buffer, which will have the size specified in the set
|
||||
* specification.
|
||||
*/
|
||||
ReturnValue_t serializeHkDataset(dp::sid_t structureId, uint8_t* buf,
|
||||
size_t maxSize) override = 0;
|
||||
/**
|
||||
* This function is implemented by the user to specify the available housekeeping sets.
|
||||
*/
|
||||
ReturnValue_t specifyHkDatasets(std::vector<hk::SetSpecification>& setList) override = 0;
|
||||
|
||||
/*
|
||||
* If this device handler has an optional pool, return it. Otherwise, nullptr can be returned.
|
||||
*/
|
||||
datapool::SharedPool* getOptionalSharedPool() override = 0;
|
||||
|
||||
/**
|
||||
* Implemented by child class. Handle all command messages which are
|
||||
* not health, mode, action or housekeeping messages.
|
||||
@@ -159,18 +174,7 @@ class FreshDeviceHandlerBase : public SystemObject,
|
||||
*/
|
||||
virtual ReturnValue_t handleCommandMessage(CommandMessage* message) = 0;
|
||||
|
||||
// HK manager abstract functions.
|
||||
LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override = 0;
|
||||
ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||
LocalDataPoolManager& poolManager) override = 0;
|
||||
|
||||
/**
|
||||
* HasModesIF implementation to check the validity of a mode command.
|
||||
* @param mode
|
||||
* @param submode
|
||||
* @param msToReachTheMode
|
||||
* @return
|
||||
*/
|
||||
// Mode abstract functions
|
||||
ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
|
||||
uint32_t* msToReachTheMode) override = 0;
|
||||
// Health Overrides.
|
||||
@@ -202,7 +206,6 @@ class FreshDeviceHandlerBase : public SystemObject,
|
||||
* @return
|
||||
*/
|
||||
virtual ReturnValue_t performOperation(uint8_t opCode) override;
|
||||
ReturnValue_t initializeAfterTaskCreation() override;
|
||||
|
||||
/**
|
||||
* This calls the FDIR instance event trigger function.
|
||||
|
||||
@@ -14,8 +14,6 @@ enum Severity : EventSeverity_t { INFO = 1, LOW = 2, MEDIUM = 3, HIGH = 4 };
|
||||
|
||||
} // namespace severity
|
||||
|
||||
#define MAKE_EVENT(id, severity) (((severity) << 16) + (SUBSYSTEM_ID * 100) + (id))
|
||||
|
||||
typedef uint32_t Event;
|
||||
|
||||
namespace event {
|
||||
@@ -24,11 +22,14 @@ constexpr EventId_t getEventId(Event event) { return (event & 0xFFFF); }
|
||||
|
||||
constexpr EventSeverity_t getSeverity(Event event) { return ((event >> 16) & 0xFF); }
|
||||
|
||||
constexpr Event makeEvent(uint8_t subsystemId, UniqueEventId_t uniqueEventId,
|
||||
EventSeverity_t eventSeverity) {
|
||||
template <uint8_t subsystemId, UniqueEventId_t uniqueEventId, EventSeverity_t eventSeverity>
|
||||
constexpr Event makeEvent() {
|
||||
static_assert(uniqueEventId < 100, "The unique event ID must be smaller than 100!");
|
||||
return (eventSeverity << 16) + (subsystemId * 100) + uniqueEventId;
|
||||
}
|
||||
|
||||
} // namespace event
|
||||
|
||||
#define MAKE_EVENT(id, severity) event::makeEvent<SUBSYSTEM_ID, id, severity>();
|
||||
|
||||
#endif /* EVENTOBJECT_EVENT_H_ */
|
||||
@@ -2,6 +2,7 @@ target_sources(
|
||||
${LIB_FSFW_NAME}
|
||||
PRIVATE arrayprinter.cpp
|
||||
AsciiConverter.cpp
|
||||
CobsEncoder.cpp
|
||||
CRC.cpp
|
||||
DleEncoder.cpp
|
||||
DleParser.cpp
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "fsfw/globalfunctions/CobsEncoder.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
ReturnValue_t CobsEncoder::encode(const uint8_t* sourceStream, size_t sourceLen,
|
||||
uint8_t* destStream, size_t maxDestLen, size_t* encodedLen) {
|
||||
*encodedLen = 0;
|
||||
|
||||
if (maxDestLen < worstCaseEncodedLen(sourceLen)) return INSUFFICIENT_SPACE;
|
||||
|
||||
size_t codeIdx = 0;
|
||||
size_t outIdx = 1;
|
||||
uint8_t code = 1;
|
||||
|
||||
for (size_t i = 0; i < sourceLen; ++i) {
|
||||
const auto isZero = sourceStream[i] == 0x00;
|
||||
|
||||
if (not isZero) {
|
||||
destStream[outIdx++] = sourceStream[i];
|
||||
code += 1;
|
||||
}
|
||||
|
||||
if (isZero or code == CobsEncoder::MAX_BLOCK_LEN + 1) { // + delimiter
|
||||
destStream[codeIdx] = code;
|
||||
codeIdx = outIdx++;
|
||||
code = 1;
|
||||
}
|
||||
}
|
||||
|
||||
destStream[codeIdx] = code;
|
||||
destStream[outIdx++] = 0x00;
|
||||
|
||||
*encodedLen = outIdx;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
ReturnValue_t CobsEncoder::decode(const uint8_t* sourceStream, size_t sourceLen, size_t* readLen,
|
||||
uint8_t* destStream, size_t maxDestLen, size_t* decodedLen) {
|
||||
*readLen = 0;
|
||||
*decodedLen = 0;
|
||||
|
||||
if (sourceLen == 0) return NO_DATA_AVAILABLE;
|
||||
|
||||
// COBS, so 0 byte never occures. Used as delimiters.
|
||||
const auto* delimiter = static_cast<const uint8_t*>(std::memchr(sourceStream, 0x00, sourceLen));
|
||||
if (delimiter == nullptr) return STREAM_TOO_SHORT;
|
||||
|
||||
const size_t frameLen = delimiter - sourceStream;
|
||||
const size_t consumedLen = frameLen + 1; // + delimiter
|
||||
|
||||
size_t inIdx = 0;
|
||||
size_t outIdx = 0;
|
||||
|
||||
while (inIdx < frameLen) {
|
||||
const size_t blockLen = sourceStream[inIdx++] - 1; // COBS, so for all bytes > 0
|
||||
const auto isFullBlock = blockLen == MAX_BLOCK_LEN;
|
||||
|
||||
// COBS data block is bogus => bad data (set readLen != 0)
|
||||
if (inIdx + blockLen > frameLen) return *readLen = consumedLen, DECODING_ERROR;
|
||||
// Data block won't fit into provided buffer
|
||||
if (maxDestLen < outIdx + blockLen) return INSUFFICIENT_SPACE;
|
||||
|
||||
std::memcpy(destStream + outIdx, sourceStream + inIdx, blockLen);
|
||||
inIdx += blockLen;
|
||||
outIdx += blockLen;
|
||||
|
||||
// COBS: != 0xFF, then meant to be zero
|
||||
if (not isFullBlock and inIdx < frameLen) {
|
||||
if (maxDestLen < outIdx + 1) return INSUFFICIENT_SPACE;
|
||||
destStream[outIdx++] = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
*readLen = consumedLen;
|
||||
*decodedLen = outIdx;
|
||||
return returnvalue::OK;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#ifndef FSFW_GLOBALFUNCTIONS_COBSENCODER_H_
|
||||
#define FSFW_GLOBALFUNCTIONS_COBSENCODER_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "fsfw/returnvalues/returnvalue.h"
|
||||
|
||||
/**
|
||||
* @brief This COBS Encoder (Consistent Overhead Byte Stuffing) can be used to encode and
|
||||
* decode arbitrary data.
|
||||
*
|
||||
* @details
|
||||
* Protocol information: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
|
||||
*
|
||||
* A COBS frame contains no zero bytes but is terminated by one, so frames can be picked out of a
|
||||
* byte stream by looking for that delimiter.
|
||||
*/
|
||||
class CobsEncoder {
|
||||
public:
|
||||
CobsEncoder() = delete;
|
||||
virtual ~CobsEncoder() = delete;
|
||||
|
||||
static constexpr uint8_t INTERFACE_ID = CLASS_ID::COBS_ENCODER;
|
||||
|
||||
/** The source stream holds no frame delimiter yet, so the frame may still be arriving.
|
||||
* Nothing was consumed and the caller should retry once more data has been received. */
|
||||
static constexpr ReturnValue_t STREAM_TOO_SHORT = MAKE_RETURN_CODE(1);
|
||||
/** The frame is delimited but its block structure is malformed, so it cannot be recovered.
|
||||
* `readLen` skips past the whole frame so decoding can resynchronise on the next one. */
|
||||
static constexpr ReturnValue_t DECODING_ERROR = MAKE_RETURN_CODE(2);
|
||||
/** The input stream is empty. */
|
||||
static constexpr ReturnValue_t NO_DATA_AVAILABLE = MAKE_RETURN_CODE(3);
|
||||
/** The output buffer is not large enough to fit the input data. */
|
||||
static constexpr ReturnValue_t INSUFFICIENT_SPACE = MAKE_RETURN_CODE(4);
|
||||
|
||||
/** Longest run of data bytes a single code byte can describe. */
|
||||
static constexpr size_t MAX_BLOCK_LEN = 254;
|
||||
|
||||
/**
|
||||
* Upper bound on the encoded size of `sourceLen` bytes, including the frame delimiter.
|
||||
* Exact when the input holds no zero bytes and at most one byte per full block too large
|
||||
* otherwise.
|
||||
* Use this during compiletime to create a correctly sized output buffer.
|
||||
* @param sourceLen Max length of buffer to encode
|
||||
* @return Use as follows: `uint8_t destBuffer[worstCaseEncodedLen(srcBufferSize)];`.
|
||||
*/
|
||||
static constexpr size_t worstCaseEncodedLen(size_t sourceLen) {
|
||||
// Derivation:
|
||||
// We know: Frame length = (data bytes) + (code bytes) + (delimiter)
|
||||
// Let n be the source length, z the number of zero bytes in it and s the number of times a data
|
||||
// block is over 254 and splits.
|
||||
// We have (data bytes) = n - z, since only non-zero bytes get copied over.
|
||||
// (code bytes) = 1 + z + s, since starting code, blocks end at zero and 254 splits.
|
||||
// So, Frame length = (data bytes) + (code bytes) + (delimiter)
|
||||
// = (n - z) + (1 + z + s) + 1 = n + s + 2
|
||||
// s depends on the data, so we derive the upper bound, since we know these splits happen every
|
||||
// 254 bytes (or less since maybe there are enough zeros spread out to not need a 254 split):
|
||||
// s <= (n - z) / 254 <= n / 254. (z >= 0)
|
||||
// Thus, n + s + 2 <= n + (n / 254) + 2
|
||||
// Note: We do integer division (floor), since a 254 code only appears every FULL 254.
|
||||
return sourceLen + sourceLen / MAX_BLOCK_LEN + 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given data stream into COBS format
|
||||
* @param sourceStream Start of the source buffer
|
||||
* @param sourceLen Length of the source buffer
|
||||
* @param destStream Destination buffer
|
||||
* @param maxDestLen Maximum length of the destination buffer
|
||||
* @param encodedLen Out pointer which is written with the actual amount of data written to the
|
||||
* destination buffer.
|
||||
* @return
|
||||
* - returnvalue::OK for successful encoding operation
|
||||
* - INSUFFICIENT_SPACE if `maxDestLen` is below `worstCaseEncodedLen(sourceLen)`.
|
||||
* Note: Technically smaller sizes would fit but `worstCaseEncodedLen` is easy to compute.
|
||||
*/
|
||||
static ReturnValue_t encode(const uint8_t* sourceStream, size_t sourceLen, uint8_t* destStream,
|
||||
size_t maxDestLen, size_t* encodedLen);
|
||||
|
||||
/**
|
||||
* Converts an encoded stream back from COBS format.
|
||||
*
|
||||
* This function only ever decodes a single COBS frame.
|
||||
* To drain a stream, advance it by `readLen` and call again until `STREAM_TOO_SHORT` reports that
|
||||
* no complete frame is left.
|
||||
* Because `readLen` is also set for a corrupt frame, a damaged frame never blocks the intact ones
|
||||
* queued behind it.
|
||||
*
|
||||
* An empty frame, meaning a delimiter with no data in front of it, decodes successfully with a
|
||||
* `decodedLen` of zero.
|
||||
* Callers that treat runs of delimiters as idle filler should ignore those.
|
||||
* @param sourceStream Start of the source buffer
|
||||
* @param sourceStreamLen Length of the source buffer
|
||||
* @param readLen Out pointer which is written with the amount of data that was actually read from
|
||||
* the source buffer. Set on success and on DECODING_ERROR, both times covering the whole
|
||||
* frame including its delimiter. Left at zero otherwise.
|
||||
* @param destStream Destination buffer
|
||||
* @param maxDestStreamlen Maximum length of the destination buffer
|
||||
* @param decodedLen Out pointer which is written with the actual amount of data written to the
|
||||
* destination buffer.
|
||||
* @return
|
||||
* - returnvalue::OK for successful decode operation
|
||||
* - STREAM_TOO_SHORT if the source stream holds no complete frame yet
|
||||
* - DECODING_ERROR if the frame is delimited but malformed. Skip `readLen` bytes to resync
|
||||
* - INSUFFICIENT_SPACE if the destination buffer cannot hold the decoded frame
|
||||
* - NO_DATA_AVAILABLE if the source stream is empty
|
||||
*/
|
||||
static ReturnValue_t decode(const uint8_t* sourceStream, size_t sourceStreamLen, size_t* readLen,
|
||||
uint8_t* destStream, size_t maxDestStreamlen, size_t* decodedLen);
|
||||
};
|
||||
|
||||
#endif /* FSFW_GLOBALFUNCTIONS_COBSENCODER_H_ */
|
||||
@@ -56,26 +56,33 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, size_t maxCharPerL
|
||||
std::cout << std::dec << std::setfill(' ');
|
||||
std::cout << "]" << std::endl;
|
||||
#else
|
||||
// General format: 0x01, 0x02, 0x03 so it is number of chars times 6
|
||||
// plus line break plus small safety margin.
|
||||
char printBuffer[(size + 1) * 7 + 1] = {};
|
||||
#if FSFW_DISABLE_PRINTOUT == 0
|
||||
// Emitted in fixed size chunks. This used to size one buffer from the input - a variable length
|
||||
// array of (size + 1) * 7 + 1 bytes on the stack - which is unusable for the sizes this is
|
||||
// actually called with: dumping a 12 KB receive buffer asks for 84 KB of stack, and overflowed
|
||||
// an 8 KB task on the iOBC as soon as a frame parse error made it dump one. The output is
|
||||
// unchanged, it is just flushed as it is built.
|
||||
constexpr size_t CHUNK_LEN = 128;
|
||||
// An entry appends at most two hex digits, a separator and a line break.
|
||||
constexpr size_t MAX_ENTRY_LEN = 4;
|
||||
char printBuffer[CHUNK_LEN] = {};
|
||||
size_t currentPos = 0;
|
||||
printf("hex [");
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
// To avoid buffer overflows.
|
||||
if (sizeof(printBuffer) - currentPos <= 7) {
|
||||
break;
|
||||
if (currentPos + MAX_ENTRY_LEN >= CHUNK_LEN) {
|
||||
printf("%s", printBuffer);
|
||||
printBuffer[0] = '\0';
|
||||
currentPos = 0;
|
||||
}
|
||||
|
||||
currentPos += snprintf(printBuffer + currentPos, 6, "%02x", data[i]);
|
||||
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, "%02x", data[i]);
|
||||
if (i < size - 1) {
|
||||
currentPos += sprintf(printBuffer + currentPos, ",");
|
||||
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, ",");
|
||||
if ((i + 1) % maxCharPerLine == 0) {
|
||||
currentPos += sprintf(printBuffer + currentPos, "\n");
|
||||
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#if FSFW_DISABLE_PRINTOUT == 0
|
||||
printf("hex [%s]\n", printBuffer);
|
||||
printf("%s]\n", printBuffer);
|
||||
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
|
||||
#endif
|
||||
}
|
||||
@@ -98,27 +105,30 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, size_t maxCharPerL
|
||||
}
|
||||
std::cout << "]" << std::endl;
|
||||
#else
|
||||
// General format: 32,243,-12 so it is number of chars times 4
|
||||
// plus line break plus small safety margin.
|
||||
uint16_t expectedLines = ceil((double)size / maxCharPerLine);
|
||||
char printBuffer[size * 4 + 1 + expectedLines] = {};
|
||||
#if FSFW_DISABLE_PRINTOUT == 0
|
||||
// Chunked for the same reason as printHex above: the buffer used to be a variable length array
|
||||
// sized from the input.
|
||||
constexpr size_t CHUNK_LEN = 128;
|
||||
// An entry appends at most three digits, a separator and a line break.
|
||||
constexpr size_t MAX_ENTRY_LEN = 5;
|
||||
char printBuffer[CHUNK_LEN] = {};
|
||||
size_t currentPos = 0;
|
||||
printf("dec [");
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
// To avoid buffer overflows.
|
||||
if (sizeof(printBuffer) - currentPos <= 4) {
|
||||
break;
|
||||
if (currentPos + MAX_ENTRY_LEN >= CHUNK_LEN) {
|
||||
printf("%s", printBuffer);
|
||||
printBuffer[0] = '\0';
|
||||
currentPos = 0;
|
||||
}
|
||||
|
||||
currentPos += snprintf(printBuffer + currentPos, 4, "%d", data[i]);
|
||||
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, "%d", data[i]);
|
||||
if (i < size - 1) {
|
||||
currentPos += sprintf(printBuffer + currentPos, ",");
|
||||
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, ",");
|
||||
if ((i + 1) % maxCharPerLine == 0) {
|
||||
currentPos += sprintf(printBuffer + currentPos, "\n");
|
||||
currentPos += snprintf(printBuffer + currentPos, CHUNK_LEN - currentPos, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#if FSFW_DISABLE_PRINTOUT == 0
|
||||
printf("dec [%s]\n", printBuffer);
|
||||
printf("%s]\n", printBuffer);
|
||||
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -27,13 +27,19 @@ class HasHealthIF {
|
||||
static const Event CHILD_PROBLEMS = MAKE_EVENT(8, severity::LOW);
|
||||
//! Assembly overwrites health information of children to keep satellite alive.
|
||||
static const Event OVERWRITING_HEALTH = MAKE_EVENT(9, severity::LOW);
|
||||
//! Someone starts a recovery of a component (typically power-cycle). No parameters.
|
||||
//! Someone starts a recovery of a component (typically power-cycle).
|
||||
//! P1: Object Id of the recovering device.
|
||||
static const Event TRYING_RECOVERY = MAKE_EVENT(10, severity::MEDIUM);
|
||||
//! Recovery is ongoing. Comes twice during recovery.
|
||||
//! P1: 0 for the first, 1 for the second event. P2: 0
|
||||
static const Event RECOVERY_STEP = MAKE_EVENT(11, severity::MEDIUM);
|
||||
//! Recovery was completed. Not necessarily successful. No parameters.
|
||||
//! P1: Object Id of the recovering device.
|
||||
static const Event RECOVERY_DONE = MAKE_EVENT(12, severity::MEDIUM);
|
||||
//! Recovery is ongoing. The recovering device is currently OFF, waiting for restart.
|
||||
//! P1: Object Id of the recovering device.
|
||||
static const Event RECOVERY_WAITING = MAKE_EVENT(13, severity::MEDIUM);
|
||||
//! Recovery is ongoing. Restarting the recovering device.
|
||||
//! P1: Object Id of the recovering device.
|
||||
static const Event RECOVERY_RESTARTING = MAKE_EVENT(14, severity::MEDIUM);
|
||||
|
||||
virtual ~HasHealthIF() {}
|
||||
|
||||
virtual MessageQueueId_t getCommandQueue() const = 0;
|
||||
|
||||
@@ -17,11 +17,11 @@ void HealthTable::setMutexTimeout(MutexIF::TimeoutType timeoutType, uint32_t tim
|
||||
HealthTable::~HealthTable() { MutexFactory::instance()->deleteMutex(mutex); }
|
||||
|
||||
ReturnValue_t HealthTable::registerObject(object_id_t object,
|
||||
HasHealthIF::HealthState initilialState) {
|
||||
HasHealthIF::HealthState initialState) {
|
||||
if (healthMap.count(object) != 0) {
|
||||
return returnvalue::FAILED;
|
||||
}
|
||||
healthMap.emplace(object, initilialState);
|
||||
healthMap.emplace(object, initialState);
|
||||
return returnvalue::OK;
|
||||
}
|
||||
|
||||
@@ -112,3 +112,5 @@ ReturnValue_t HealthTable::iterate(HealthEntry* value, bool reset) {
|
||||
mapIterator++;
|
||||
return result;
|
||||
}
|
||||
|
||||
MutexIF* HealthTable::getMutex() { return mutex; }
|
||||
@@ -18,7 +18,7 @@ class HealthTable : public HealthTableIF, public SystemObject {
|
||||
|
||||
/** HealthTableIF overrides */
|
||||
virtual ReturnValue_t registerObject(
|
||||
object_id_t object, HasHealthIF::HealthState initilialState = HasHealthIF::HEALTHY) override;
|
||||
object_id_t object, HasHealthIF::HealthState initialState = HasHealthIF::HEALTHY) override;
|
||||
ReturnValue_t removeObject(object_id_t object) override;
|
||||
virtual size_t getPrintSize() override;
|
||||
virtual void printAll(uint8_t* pointer, size_t maxSize) override;
|
||||
@@ -28,6 +28,8 @@ class HealthTable : public HealthTableIF, public SystemObject {
|
||||
virtual void setHealth(object_id_t object, HasHealthIF::HealthState newState) override;
|
||||
virtual HasHealthIF::HealthState getHealth(object_id_t) override;
|
||||
|
||||
MutexIF* getMutex();
|
||||
|
||||
protected:
|
||||
using HealthMap = std::map<object_id_t, HasHealthIF::HealthState>;
|
||||
using HealthEntry = std::pair<object_id_t, HasHealthIF::HealthState>;
|
||||
|
||||
@@ -12,7 +12,7 @@ class HealthTableIF : public ManagesHealthIF {
|
||||
virtual ~HealthTableIF() {}
|
||||
|
||||
virtual ReturnValue_t registerObject(
|
||||
object_id_t object, HasHealthIF::HealthState initilialState = HasHealthIF::HEALTHY) = 0;
|
||||
object_id_t object, HasHealthIF::HealthState initialState = HasHealthIF::HEALTHY) = 0;
|
||||
|
||||
virtual ReturnValue_t removeObject(object_id_t objectId) = 0;
|
||||
|
||||
|
||||
Loaded 100 of 274 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user