From f0ca4be71eec5e3883d270e98b5b53ceab4fe1e2 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 13:09:03 +0100 Subject: [PATCH 01/22] fsfw version include removed --- defaultcfg/fsfwconfig/FSFWConfig.h | 1 - 1 file changed, 1 deletion(-) diff --git a/defaultcfg/fsfwconfig/FSFWConfig.h b/defaultcfg/fsfwconfig/FSFWConfig.h index fa769bad..f475e3fa 100644 --- a/defaultcfg/fsfwconfig/FSFWConfig.h +++ b/defaultcfg/fsfwconfig/FSFWConfig.h @@ -1,7 +1,6 @@ #ifndef CONFIG_FSFWCONFIG_H_ #define CONFIG_FSFWCONFIG_H_ -#include #include #include From fa4597782ec20090f45d390c9cee6542561de7f6 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 13:09:56 +0100 Subject: [PATCH 02/22] some improvements taken over from master --- osal/linux/TcUnixUdpPollingTask.cpp | 2 +- osal/linux/TmTcUnixUdpBridge.cpp | 16 ++++++++++++---- osal/linux/TmTcUnixUdpBridge.h | 5 ++++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/osal/linux/TcUnixUdpPollingTask.cpp b/osal/linux/TcUnixUdpPollingTask.cpp index 670ba804..af99ec91 100644 --- a/osal/linux/TcUnixUdpPollingTask.cpp +++ b/osal/linux/TcUnixUdpPollingTask.cpp @@ -33,7 +33,7 @@ ReturnValue_t TcUnixUdpPollingTask::performOperation(uint8_t opCode) { while(1) { //! Sender Address is cached here. struct sockaddr_in senderAddress; - socklen_t senderSockLen = 0; + socklen_t senderSockLen = sizeof(senderAddress); ssize_t bytesReceived = recvfrom(serverUdpSocket, receptionBuffer.data(), frameSize, receptionFlags, reinterpret_cast(&senderAddress), &senderSockLen); diff --git a/osal/linux/TmTcUnixUdpBridge.cpp b/osal/linux/TmTcUnixUdpBridge.cpp index b55291b3..ab28623e 100644 --- a/osal/linux/TmTcUnixUdpBridge.cpp +++ b/osal/linux/TmTcUnixUdpBridge.cpp @@ -65,9 +65,13 @@ TmTcUnixUdpBridge::~TmTcUnixUdpBridge() { ReturnValue_t TmTcUnixUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { int flags = 0; - clientAddress.sin_addr.s_addr = htons(INADDR_ANY); - //clientAddress.sin_addr.s_addr = inet_addr("127.73.73.1"); - clientAddressLen = sizeof(serverAddress); + MutexHelper lock(mutex, MutexIF::TimeoutType::WAITING, 10); + + if(ipAddrAnySet){ + clientAddress.sin_addr.s_addr = htons(INADDR_ANY); + //clientAddress.sin_addr.s_addr = inet_addr("127.73.73.1"); + clientAddressLen = sizeof(serverAddress); + } // char ipAddress [15]; // sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, @@ -85,7 +89,7 @@ ReturnValue_t TmTcUnixUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { return HasReturnvaluesIF::RETURN_OK; } -void TmTcUnixUdpBridge::checkAndSetClientAddress(sockaddr_in newAddress) { +void TmTcUnixUdpBridge::checkAndSetClientAddress(sockaddr_in& newAddress) { MutexHelper lock(mutex, MutexIF::TimeoutType::WAITING, 10); // char ipAddress [15]; @@ -168,3 +172,7 @@ void TmTcUnixUdpBridge::handleSendError() { } } +void TmTcUnixUdpBridge::setClientAddressToAny(bool ipAddrAnySet){ + this->ipAddrAnySet = ipAddrAnySet; +} + diff --git a/osal/linux/TmTcUnixUdpBridge.h b/osal/linux/TmTcUnixUdpBridge.h index 5245c44c..ae6f6adc 100644 --- a/osal/linux/TmTcUnixUdpBridge.h +++ b/osal/linux/TmTcUnixUdpBridge.h @@ -20,8 +20,9 @@ public: uint16_t serverPort = 0xFFFF,uint16_t clientPort = 0xFFFF); virtual~ TmTcUnixUdpBridge(); - void checkAndSetClientAddress(sockaddr_in clientAddress); + void checkAndSetClientAddress(sockaddr_in& clientAddress); + void setClientAddressToAny(bool ipAddrAnySet); protected: virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override; @@ -36,6 +37,8 @@ private: struct sockaddr_in serverAddress; socklen_t serverAddressLen = 0; + bool ipAddrAnySet = false; + //! Access to the client address is mutex protected as it is set //! by another task. MutexIF* mutex; From b7945dfe606e2b2edd1a618aa089f893876ed75c Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 13:45:08 +0100 Subject: [PATCH 03/22] added gp_id_t --- datapoollocal/HasLocalDataPoolIF.h | 3 +- datapoollocal/locPoolDefinitions.h | 44 +++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/datapoollocal/HasLocalDataPoolIF.h b/datapoollocal/HasLocalDataPoolIF.h index 9d4f2605..7f1f202e 100644 --- a/datapoollocal/HasLocalDataPoolIF.h +++ b/datapoollocal/HasLocalDataPoolIF.h @@ -43,7 +43,8 @@ public: virtual~ HasLocalDataPoolIF() {}; static constexpr uint8_t INTERFACE_ID = CLASS_ID::LOCAL_POOL_OWNER_IF; - static constexpr lp_id_t NO_POOL_ID = 0xffffffff; + + static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID; virtual object_id_t getObjectId() const = 0; diff --git a/datapoollocal/locPoolDefinitions.h b/datapoollocal/locPoolDefinitions.h index d1493059..1115a336 100644 --- a/datapoollocal/locPoolDefinitions.h +++ b/datapoollocal/locPoolDefinitions.h @@ -10,11 +10,19 @@ */ using lp_id_t = uint32_t; +namespace localpool { +static constexpr uint32_t INVALID_LPID = -1; +} +/** + * Used as a unique identifier for data sets. + */ union sid_t { static constexpr uint64_t INVALID_SID = -1; - static constexpr uint32_t INVALID_SET_ID = -1; static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT; + static constexpr uint32_t INVALID_SET_ID = -1; + + sid_t(): raw(INVALID_SID) {} sid_t(object_id_t objectId, uint32_t setId): @@ -48,4 +56,38 @@ union sid_t { } }; +/** + * Used as a global unique identifier for local pool variables. + */ +union gp_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) {} + + gp_id_t(object_id_t objectID, lp_id_t localPoolId): + objectId(objectId), + localPoolId(localPoolId) {} + + struct { + object_id_t objectId; + lp_id_t localPoolId; + }; + + uint64_t raw; + + bool notSet() const { + return raw == INVALID_GPID; + } + + bool operator==(const sid_t& other) const { + return raw == other.raw; + } + + bool operator!=(const sid_t& other) const { + return not (raw == other.raw); + } +}; + #endif /* FSFW_DATAPOOLLOCAL_LOCPOOLDEFINITIONS_H_ */ From 25f08b8c1f8143bbcabfab7f40541351eb2adc50 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 13:59:54 +0100 Subject: [PATCH 04/22] started replacing local pool vars --- datapoollocal/LocalPoolObjectBase.cpp | 2 +- datapoollocal/LocalPoolObjectBase.h | 2 +- datapoollocal/LocalPoolVariable.h | 11 +++++++++-- datapoollocal/LocalPoolVariable.tpp | 12 ++++++++++-- datapoollocal/LocalPoolVector.h | 18 ++++++++++++++---- datapoollocal/LocalPoolVector.tpp | 21 ++++++++++++++------- thermal/ThermalComponentCore.cpp | 7 +++---- thermal/ThermalComponentCore.h | 19 ++++++++++--------- 8 files changed, 62 insertions(+), 30 deletions(-) diff --git a/datapoollocal/LocalPoolObjectBase.cpp b/datapoollocal/LocalPoolObjectBase.cpp index 88027169..c25df25a 100644 --- a/datapoollocal/LocalPoolObjectBase.cpp +++ b/datapoollocal/LocalPoolObjectBase.cpp @@ -19,7 +19,7 @@ LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, } } -LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, object_id_t poolOwner, +LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode): localPoolId(poolId), readWriteMode(setReadWriteMode) { if(poolId == PoolVariableIF::NO_PARAMETER) { diff --git a/datapoollocal/LocalPoolObjectBase.h b/datapoollocal/LocalPoolObjectBase.h index 4d25b88a..8d1ab45e 100644 --- a/datapoollocal/LocalPoolObjectBase.h +++ b/datapoollocal/LocalPoolObjectBase.h @@ -14,7 +14,7 @@ public: HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet, pool_rwm_t setReadWriteMode); - LocalPoolObjectBase(lp_id_t poolId, object_id_t poolOwner, + LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr, pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); diff --git a/datapoollocal/LocalPoolVariable.h b/datapoollocal/LocalPoolVariable.h index e9d94178..85d86420 100644 --- a/datapoollocal/LocalPoolVariable.h +++ b/datapoollocal/LocalPoolVariable.h @@ -64,9 +64,17 @@ public: * @param setReadWriteMode Specify the read-write mode of the pool variable. * */ - LocalPoolVar(lp_id_t poolId, object_id_t poolOwner, + LocalPoolVar(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 + */ + LocalPoolVar(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); virtual~ LocalPoolVar() {}; @@ -156,5 +164,4 @@ using lp_int64_t = LocalPoolVar; using lp_float_t = LocalPoolVar; using lp_double_t = LocalPoolVar; - #endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ */ diff --git a/datapoollocal/LocalPoolVariable.tpp b/datapoollocal/LocalPoolVariable.tpp index 3320aa5a..7025e462 100644 --- a/datapoollocal/LocalPoolVariable.tpp +++ b/datapoollocal/LocalPoolVariable.tpp @@ -12,9 +12,17 @@ inline LocalPoolVar::LocalPoolVar(lp_id_t poolId, LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {} template -inline LocalPoolVar::LocalPoolVar(lp_id_t poolId, object_id_t poolOwner, +inline LocalPoolVar::LocalPoolVar(object_id_t poolOwner, lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode): - LocalPoolObjectBase(poolId, poolOwner, dataSet, setReadWriteMode) {} + LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {} + + +template +inline LocalPoolVar::LocalPoolVar(gp_id_t globalPoolId, DataSetIF *dataSet, + pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, + dataSet, setReadWriteMode){} + template inline ReturnValue_t LocalPoolVar::read(dur_millis_t lockTimeout) { diff --git a/datapoollocal/LocalPoolVector.h b/datapoollocal/LocalPoolVector.h index a7ebd3fe..4644b748 100644 --- a/datapoollocal/LocalPoolVector.h +++ b/datapoollocal/LocalPoolVector.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ -#define FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ #include "LocalPoolObjectBase.h" #include "../datapool/DataSetIF.h" @@ -65,7 +65,17 @@ public: * @param dataSet The data set in which the variable shall register itself. * If nullptr, the variable is not registered. */ - LocalPoolVector(lp_id_t poolId, object_id_t poolOwner, + LocalPoolVector(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 unique global identifier of a local pool + * vector. + * @param globalPoolId + * @param dataSet + * @param setReadWriteMode + */ + LocalPoolVector(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr, pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); @@ -161,4 +171,4 @@ private: template using lp_vec_t = LocalPoolVector; -#endif /* FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ */ +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ */ diff --git a/datapoollocal/LocalPoolVector.tpp b/datapoollocal/LocalPoolVector.tpp index 34720b97..fc3c0a2e 100644 --- a/datapoollocal/LocalPoolVector.tpp +++ b/datapoollocal/LocalPoolVector.tpp @@ -1,7 +1,7 @@ -#ifndef FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ -#define FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ -#ifndef FRAMEWORK_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ #error Include LocalPoolVector.h before LocalPoolVector.tpp! #endif @@ -12,9 +12,16 @@ inline LocalPoolVector::LocalPoolVector(lp_id_t poolId, LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {} template -inline LocalPoolVector::LocalPoolVector(lp_id_t poolId, - object_id_t poolOwner, DataSetIF *dataSet, pool_rwm_t setReadWriteMode): - LocalPoolObjectBase(poolId, poolOwner, dataSet, setReadWriteMode) {} +inline LocalPoolVector::LocalPoolVector(object_id_t poolOwner, + lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {} + + +template +inline LocalPoolVector::LocalPoolVector(gp_id_t globalPoolId, + DataSetIF *dataSet, pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, + dataSet, setReadWriteMode) {} template inline ReturnValue_t LocalPoolVector::read(uint32_t lockTimeout) { @@ -148,4 +155,4 @@ inline std::ostream& operator<< (std::ostream &out, return out; } -#endif +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ */ diff --git a/thermal/ThermalComponentCore.cpp b/thermal/ThermalComponentCore.cpp index bf28f307..07649bf3 100644 --- a/thermal/ThermalComponentCore.cpp +++ b/thermal/ThermalComponentCore.cpp @@ -1,10 +1,9 @@ #include "ThermalComponentCore.h" ThermalComponentCore::ThermalComponentCore(object_id_t reportingObjectId, - uint8_t domainId, uint32_t temperaturePoolId, - uint32_t targetStatePoolId, - uint32_t currentStatePoolId, uint32_t requestPoolId, - GlobDataSet* dataSet, Parameters parameters, + uint8_t domainId, gp_id_t temperaturePoolId, + gp_id_t targetStatePoolId, gp_id_t currentStatePoolId, + gp_id_t requestPoolId, LocalDataSetBase* dataSet, Parameters parameters, StateRequest initialTargetState) : temperature(temperaturePoolId, dataSet, PoolVariableIF::VAR_WRITE), targetState(targetStatePoolId, dataSet, PoolVariableIF::VAR_READ), diff --git a/thermal/ThermalComponentCore.h b/thermal/ThermalComponentCore.h index 2d08ed1d..49ca098e 100644 --- a/thermal/ThermalComponentCore.h +++ b/thermal/ThermalComponentCore.h @@ -6,8 +6,9 @@ #include "AbstractTemperatureSensor.h" #include "ThermalModule.h" -#include "../datapoolglob/GlobalDataSet.h" -#include "../datapoolglob/GlobalPoolVariable.h" +//#include "../datapoolglob/GlobalDataSet.h" +//#include "../datapoolglob/GlobalPoolVariable.h" +#include "../datapoollocal/LocalPoolVariable.h" /** * @brief @@ -38,9 +39,9 @@ public: * @param initialTargetState */ ThermalComponentCore(object_id_t reportingObjectId, uint8_t domainId, - uint32_t temperaturePoolId, uint32_t targetStatePoolId, - uint32_t currentStatePoolId, uint32_t requestPoolId, - GlobDataSet *dataSet, Parameters parameters, + gp_id_t temperaturePoolId, gp_id_t targetStatePoolId, + gp_id_t currentStatePoolId, gp_id_t requestPoolId, + LocalDataSetBase* dataSet, Parameters parameters, StateRequest initialTargetState = ThermalComponentIF::STATE_REQUEST_OPERATIONAL); @@ -80,10 +81,10 @@ protected: AbstractTemperatureSensor *secondRedundantSensor = nullptr; ThermalModuleIF *thermalModule = nullptr; - gp_float_t temperature; - gp_int8_t targetState; - gp_int8_t currentState; - gp_uint8_t heaterRequest; + lp_var_t temperature; + lp_var_t targetState; + lp_var_t currentState; + lp_var_t heaterRequest; bool isHeating = false; From e7a96d5ad8dd7169818e624943bcf15b651c0af1 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 14:13:52 +0100 Subject: [PATCH 05/22] it doesnt work? comment it ! --- datapoollocal/LocalDataPoolManager.h | 2 +- datapoollocal/LocalPoolVariable.h | 2 +- datapoollocal/LocalPoolVariable.tpp | 5 +- internalError/InternalErrorDataset.h | 12 +-- thermal/ThermalComponent.cpp | 18 ++-- thermal/ThermalComponent.h | 6 +- thermal/ThermalComponentCore.cpp | 147 ++++++++++++++------------- thermal/ThermalComponentCore.h | 6 +- 8 files changed, 99 insertions(+), 99 deletions(-) diff --git a/datapoollocal/LocalDataPoolManager.h b/datapoollocal/LocalDataPoolManager.h index aca0d4f6..95d48303 100644 --- a/datapoollocal/LocalDataPoolManager.h +++ b/datapoollocal/LocalDataPoolManager.h @@ -20,7 +20,7 @@ namespace Factory { void setStaticFrameworkObjectIds(); } -class LocalDataSetBase; +class LocalPoolDataSetBase; class HousekeepingPacketUpdate; /** diff --git a/datapoollocal/LocalPoolVariable.h b/datapoollocal/LocalPoolVariable.h index 85d86420..75cf570e 100644 --- a/datapoollocal/LocalPoolVariable.h +++ b/datapoollocal/LocalPoolVariable.h @@ -43,7 +43,7 @@ public: * If nullptr, the variable is not registered. * @param setReadWriteMode Specify the read-write mode of the pool variable. */ - LocalPoolVar(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, + LocalPoolVar(HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr, pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); diff --git a/datapoollocal/LocalPoolVariable.tpp b/datapoollocal/LocalPoolVariable.tpp index 7025e462..26500e3b 100644 --- a/datapoollocal/LocalPoolVariable.tpp +++ b/datapoollocal/LocalPoolVariable.tpp @@ -6,9 +6,8 @@ #endif template -inline LocalPoolVar::LocalPoolVar(lp_id_t poolId, - HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet, - pool_rwm_t setReadWriteMode): +inline LocalPoolVar::LocalPoolVar(HasLocalDataPoolIF* hkOwner, + lp_id_t poolId, DataSetIF* dataSet, pool_rwm_t setReadWriteMode): LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {} template diff --git a/internalError/InternalErrorDataset.h b/internalError/InternalErrorDataset.h index 52a4b632..fa91116d 100644 --- a/internalError/InternalErrorDataset.h +++ b/internalError/InternalErrorDataset.h @@ -21,12 +21,12 @@ public: InternalErrorDataset(object_id_t objectId): StaticLocalDataSet(sid_t(objectId , ERROR_SET_ID)) {} - lp_var_t tmHits = lp_var_t(TM_HITS, - hkManager->getOwner(), this); - lp_var_t queueHits = lp_var_t(QUEUE_HITS, - hkManager->getOwner(), this); - lp_var_t storeHits = lp_var_t(STORE_HITS, - hkManager->getOwner(), this); + lp_var_t tmHits = lp_var_t(hkManager->getOwner(), + TM_HITS, this); + lp_var_t queueHits = lp_var_t(hkManager->getOwner(), + QUEUE_HITS, this); + lp_var_t storeHits = lp_var_t(hkManager->getOwner(), + STORE_HITS, this); }; diff --git a/thermal/ThermalComponent.cpp b/thermal/ThermalComponent.cpp index 78b14efd..54bd67bd 100644 --- a/thermal/ThermalComponent.cpp +++ b/thermal/ThermalComponent.cpp @@ -1,9 +1,9 @@ #include "ThermalComponent.h" ThermalComponent::ThermalComponent(object_id_t reportingObjectId, - uint8_t domainId, uint32_t temperaturePoolId, - uint32_t targetStatePoolId, uint32_t currentStatePoolId, - uint32_t requestPoolId, GlobDataSet* dataSet, + uint8_t domainId, gp_id_t temperaturePoolId, + gp_id_t targetStatePoolId, gp_id_t currentStatePoolId, + gp_id_t requestPoolId, LocalPoolDataSetBase* dataSet, AbstractTemperatureSensor* sensor, AbstractTemperatureSensor* firstRedundantSensor, AbstractTemperatureSensor* secondRedundantSensor, @@ -78,13 +78,13 @@ ThermalComponentIF::State ThermalComponent::getState(float temperature, } void ThermalComponent::checkLimits(ThermalComponentIF::State state) { - if (targetState == STATE_REQUEST_OPERATIONAL || targetState == STATE_REQUEST_IGNORE) { - ThermalComponentCore::checkLimits(state); - return; - } + //if (targetState == STATE_REQUEST_OPERATIONAL || targetState == STATE_REQUEST_IGNORE) { + // ThermalComponentCore::checkLimits(state); + // return; + //} //If component is not operational, it checks the NOP limits. - temperatureMonitor.translateState(state, temperature.value, - nopParameters.lowerNopLimit, nopParameters.upperNopLimit, false); + //temperatureMonitor.translateState(state, temperature.value, + // nopParameters.lowerNopLimit, nopParameters.upperNopLimit, false); } ThermalComponentIF::HeaterRequest ThermalComponent::getHeaterRequest( diff --git a/thermal/ThermalComponent.h b/thermal/ThermalComponent.h index d61ff01e..0785a914 100644 --- a/thermal/ThermalComponent.h +++ b/thermal/ThermalComponent.h @@ -45,9 +45,9 @@ public: * @param priority */ ThermalComponent(object_id_t reportingObjectId, uint8_t domainId, - uint32_t temperaturePoolId, uint32_t targetStatePoolId, - uint32_t currentStatePoolId, uint32_t requestPoolId, - GlobDataSet *dataSet, AbstractTemperatureSensor *sensor, + gp_id_t temperaturePoolId, gp_id_t targetStatePoolId, + gp_id_t currentStatePoolId, gp_id_t requestPoolId, + LocalPoolDataSetBase *dataSet, AbstractTemperatureSensor *sensor, AbstractTemperatureSensor *firstRedundantSensor, AbstractTemperatureSensor *secondRedundantSensor, ThermalModuleIF *thermalModule, Parameters parameters, diff --git a/thermal/ThermalComponentCore.cpp b/thermal/ThermalComponentCore.cpp index 07649bf3..53eeb58f 100644 --- a/thermal/ThermalComponentCore.cpp +++ b/thermal/ThermalComponentCore.cpp @@ -3,22 +3,22 @@ ThermalComponentCore::ThermalComponentCore(object_id_t reportingObjectId, uint8_t domainId, gp_id_t temperaturePoolId, gp_id_t targetStatePoolId, gp_id_t currentStatePoolId, - gp_id_t requestPoolId, LocalDataSetBase* dataSet, Parameters parameters, - StateRequest initialTargetState) : + gp_id_t requestPoolId, LocalPoolDataSetBase* dataSet, + Parameters parameters, StateRequest initialTargetState) : temperature(temperaturePoolId, dataSet, PoolVariableIF::VAR_WRITE), targetState(targetStatePoolId, dataSet, PoolVariableIF::VAR_READ), currentState(currentStatePoolId, dataSet, PoolVariableIF::VAR_WRITE), heaterRequest(requestPoolId, dataSet, PoolVariableIF::VAR_WRITE), - parameters(parameters), - temperatureMonitor(reportingObjectId, domainId + 1, - GlobalDataPool::poolIdAndPositionToPid(temperaturePoolId, 0), - COMPONENT_TEMP_CONFIRMATION), domainId(domainId) { + parameters(parameters), domainId(domainId) { + //temperatureMonitor(reportingObjectId, domainId + 1, + // GlobalDataPool::poolIdAndPositionToPid(temperaturePoolId, 0), + // COMPONENT_TEMP_CONFIRMATION), domainId(domainId) { //Set thermal state once, then leave to operator. - GlobDataSet mySet; - gp_uint8_t writableTargetState(targetStatePoolId, &mySet, - PoolVariableIF::VAR_WRITE); - writableTargetState = initialTargetState; - mySet.commit(PoolVariableIF::VALID); + //GlobDataSet mySet; + //gp_uint8_t writableTargetState(targetStatePoolId, &mySet, + // PoolVariableIF::VAR_WRITE); + //writableTargetState = initialTargetState; + //mySet.commit(PoolVariableIF::VALID); } void ThermalComponentCore::addSensor(AbstractTemperatureSensor* sensor) { @@ -57,19 +57,19 @@ ThermalComponentIF::HeaterRequest ThermalComponentCore::performOperation( HeaterRequest request = HEATER_DONT_CARE; //SHOULDDO: Better pass db_float_t* to getTemperature and set it invalid if invalid. temperature = getTemperature(); - updateMinMaxTemp(); - if ((temperature != INVALID_TEMPERATURE)) { - temperature.setValid(PoolVariableIF::VALID); - State state = getState(temperature, getParameters(), targetState); - currentState = state; - checkLimits(state); - request = getHeaterRequest(targetState, temperature, getParameters()); - } else { - temperatureMonitor.setToInvalid(); - temperature.setValid(PoolVariableIF::INVALID); - currentState = UNKNOWN; - request = HEATER_DONT_CARE; - } + //updateMinMaxTemp(); + //if ((temperature != INVALID_TEMPERATURE)) { + //temperature.setValid(PoolVariableIF::VALID); + //State state = getState(temperature, getParameters(), targetState); + //currentState = state; + //checkLimits(state); + //request = getHeaterRequest(targetState, temperature, getParameters()); + //} else { + // temperatureMonitor.setToInvalid(); + // temperature.setValid(PoolVariableIF::INVALID); + // currentState = UNKNOWN; + // request = HEATER_DONT_CARE; + //} currentState.setValid(PoolVariableIF::VALID); heaterRequest = request; heaterRequest.setValid(PoolVariableIF::VALID); @@ -77,11 +77,12 @@ ThermalComponentIF::HeaterRequest ThermalComponentCore::performOperation( } void ThermalComponentCore::markStateIgnored() { - currentState = getIgnoredState(currentState); + //currentState = getIgnoredState(currentState); } object_id_t ThermalComponentCore::getObjectId() { - return temperatureMonitor.getReporterId(); + //return temperatureMonitor.getReporterId(); + return 0; } float ThermalComponentCore::getLowerOpLimit() { @@ -119,7 +120,7 @@ void ThermalComponentCore::setOutputInvalid() { currentState.setValid(PoolVariableIF::INVALID); heaterRequest = HEATER_DONT_CARE; heaterRequest.setValid(PoolVariableIF::INVALID); - temperatureMonitor.setToUnchecked(); + //temperatureMonitor.setToUnchecked(); } float ThermalComponentCore::getTemperature() { @@ -169,8 +170,8 @@ ThermalComponentIF::State ThermalComponentCore::getState(float temperature, void ThermalComponentCore::checkLimits(ThermalComponentIF::State state) { //Checks operational limits only. - temperatureMonitor.translateState(state, temperature.value, - getParameters().lowerOpLimit, getParameters().upperOpLimit); + //temperatureMonitor.translateState(state, temperature.value, + // getParameters().lowerOpLimit, getParameters().upperOpLimit); } @@ -220,17 +221,17 @@ ThermalComponentIF::State ThermalComponentCore::getIgnoredState(int8_t state) { } } -void ThermalComponentCore::updateMinMaxTemp() { - if (temperature == INVALID_TEMPERATURE) { - return; - } - if (temperature < minTemp) { - minTemp = temperature; - } - if (temperature > maxTemp) { - maxTemp = temperature; - } -} +//void ThermalComponentCore::updateMinMaxTemp() { +// if (temperature == INVALID_TEMPERATURE) { +// return; +// } +// if (temperature < minTemp) { +// minTemp = temperature; +// } +// if (temperature > maxTemp) { +// maxTemp = temperature; +// } +//} uint8_t ThermalComponentCore::getDomainId() const { return domainId; @@ -243,38 +244,38 @@ ThermalComponentCore::Parameters ThermalComponentCore::getParameters() { ReturnValue_t ThermalComponentCore::getParameter(uint8_t domainId, uint16_t parameterId, ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues, uint16_t startAtIndex) { - ReturnValue_t result = temperatureMonitor.getParameter(domainId, - parameterId, parameterWrapper, newValues, startAtIndex); - if (result != INVALID_DOMAIN_ID) { - return result; - } - if (domainId != this->domainId) { - return INVALID_DOMAIN_ID; - } - switch (parameterId) { - case 0: - parameterWrapper->set(parameters.heaterOn); - break; - case 1: - parameterWrapper->set(parameters.hysteresis); - break; - case 2: - parameterWrapper->set(parameters.heaterSwitchoff); - break; - case 3: - parameterWrapper->set(minTemp); - break; - case 4: - parameterWrapper->set(maxTemp); - break; - case 10: - parameterWrapper->set(parameters.lowerOpLimit); - break; - case 11: - parameterWrapper->set(parameters.upperOpLimit); - break; - default: - return INVALID_IDENTIFIER_ID; - } + //ReturnValue_t result = temperatureMonitor.getParameter(domainId, + // parameterId, parameterWrapper, newValues, startAtIndex); +// if (result != INVALID_DOMAIN_ID) { +// return result; +// } +// if (domainId != this->domainId) { +// return INVALID_DOMAIN_ID; +// } +// switch (parameterId) { +// case 0: +// parameterWrapper->set(parameters.heaterOn); +// break; +// case 1: +// parameterWrapper->set(parameters.hysteresis); +// break; +// case 2: +// parameterWrapper->set(parameters.heaterSwitchoff); +// break; +// case 3: +// parameterWrapper->set(minTemp); +// break; +// case 4: +// parameterWrapper->set(maxTemp); +// break; +// case 10: +// parameterWrapper->set(parameters.lowerOpLimit); +// break; +// case 11: +// parameterWrapper->set(parameters.upperOpLimit); +// break; +// default: +// return INVALID_IDENTIFIER_ID; +// } return HasReturnvaluesIF::RETURN_OK; } diff --git a/thermal/ThermalComponentCore.h b/thermal/ThermalComponentCore.h index 49ca098e..6ef59058 100644 --- a/thermal/ThermalComponentCore.h +++ b/thermal/ThermalComponentCore.h @@ -41,7 +41,7 @@ public: ThermalComponentCore(object_id_t reportingObjectId, uint8_t domainId, gp_id_t temperaturePoolId, gp_id_t targetStatePoolId, gp_id_t currentStatePoolId, gp_id_t requestPoolId, - LocalDataSetBase* dataSet, Parameters parameters, + LocalPoolDataSetBase* dataSet, Parameters parameters, StateRequest initialTargetState = ThermalComponentIF::STATE_REQUEST_OPERATIONAL); @@ -96,7 +96,7 @@ protected: Parameters parameters; - ThermalMonitorReporter temperatureMonitor; + //ThermalMonitorReporter temperatureMonitor; const uint8_t domainId; @@ -111,7 +111,7 @@ protected: virtual State getIgnoredState(int8_t state); - void updateMinMaxTemp(); + //void updateMinMaxTemp(); virtual Parameters getParameters(); }; From f70ee7696a82f96268d5cc06b6182a6c30780515 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 15:30:56 +0100 Subject: [PATCH 06/22] it compiles again --- datapoollocal/LocalPoolDataSetBase.cpp | 2 +- datapoollocal/LocalPoolVariable.h | 2 + datapoollocal/LocalPoolVariable.tpp | 9 +++- datapoollocal/LocalPoolVector.h | 2 +- datapoollocal/LocalPoolVector.tpp | 4 +- datapoollocal/locPoolDefinitions.h | 2 +- monitoring/AbsLimitMonitor.h | 10 ++-- monitoring/LimitMonitor.h | 12 ++--- monitoring/MonitorBase.h | 7 ++- monitoring/MonitorReporter.h | 5 +- monitoring/MonitoringMessageContent.h | 37 +++++++++------ power/Fuse.cpp | 63 +++++++++++++------------- power/Fuse.h | 32 +++++++------ power/PowerSensor.cpp | 56 ++++++++++++----------- power/PowerSensor.h | 27 +++++++---- thermal/ThermalMonitorReporter.cpp | 15 ++++-- 16 files changed, 162 insertions(+), 123 deletions(-) diff --git a/datapoollocal/LocalPoolDataSetBase.cpp b/datapoollocal/LocalPoolDataSetBase.cpp index 6e237c48..640956db 100644 --- a/datapoollocal/LocalPoolDataSetBase.cpp +++ b/datapoollocal/LocalPoolDataSetBase.cpp @@ -31,7 +31,7 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner, LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray, const size_t maxNumberOfVariables): - PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { + PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { HasLocalDataPoolIF* hkOwner = objectManager->get( sid.objectId); if(hkOwner == nullptr) { diff --git a/datapoollocal/LocalPoolVariable.h b/datapoollocal/LocalPoolVariable.h index 75cf570e..173c2035 100644 --- a/datapoollocal/LocalPoolVariable.h +++ b/datapoollocal/LocalPoolVariable.h @@ -119,6 +119,7 @@ public: LocalPoolVar &operator=(T newValue); + LocalPoolVar &operator=(LocalPoolVar newPoolVariable); protected: /** * @brief Like #read, but without a lock protection of the global pool. @@ -164,4 +165,5 @@ using lp_int64_t = LocalPoolVar; using lp_float_t = LocalPoolVar; using lp_double_t = LocalPoolVar; + #endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ */ diff --git a/datapoollocal/LocalPoolVariable.tpp b/datapoollocal/LocalPoolVariable.tpp index 26500e3b..8aa164d1 100644 --- a/datapoollocal/LocalPoolVariable.tpp +++ b/datapoollocal/LocalPoolVariable.tpp @@ -81,11 +81,18 @@ inline ReturnValue_t LocalPoolVar::commitWithoutLock() { } template -inline LocalPoolVar & LocalPoolVar::operator =(T newValue) { +inline LocalPoolVar & LocalPoolVar::operator=(T newValue) { value = newValue; return *this; } +template +inline LocalPoolVar& LocalPoolVar::operator =( + LocalPoolVar newPoolVariable) { + value = newPoolVariable.value; + return *this; +} + template inline ReturnValue_t LocalPoolVar::serialize(uint8_t** buffer, size_t* size, diff --git a/datapoollocal/LocalPoolVector.h b/datapoollocal/LocalPoolVector.h index 4644b748..58face3c 100644 --- a/datapoollocal/LocalPoolVector.h +++ b/datapoollocal/LocalPoolVector.h @@ -47,7 +47,7 @@ public: * @param dataSet The data set in which the variable shall register itself. * If nullptr, the variable is not registered. */ - LocalPoolVector(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, + LocalPoolVector(HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr, pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); diff --git a/datapoollocal/LocalPoolVector.tpp b/datapoollocal/LocalPoolVector.tpp index fc3c0a2e..46123ccc 100644 --- a/datapoollocal/LocalPoolVector.tpp +++ b/datapoollocal/LocalPoolVector.tpp @@ -6,8 +6,8 @@ #endif template -inline LocalPoolVector::LocalPoolVector(lp_id_t poolId, - HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet, +inline LocalPoolVector::LocalPoolVector( + HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet, pool_rwm_t setReadWriteMode): LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {} diff --git a/datapoollocal/locPoolDefinitions.h b/datapoollocal/locPoolDefinitions.h index 1115a336..6e74d349 100644 --- a/datapoollocal/locPoolDefinitions.h +++ b/datapoollocal/locPoolDefinitions.h @@ -66,7 +66,7 @@ union gp_id_t { gp_id_t(): raw(INVALID_GPID) {} - gp_id_t(object_id_t objectID, lp_id_t localPoolId): + gp_id_t(object_id_t objectId, lp_id_t localPoolId): objectId(objectId), localPoolId(localPoolId) {} diff --git a/monitoring/AbsLimitMonitor.h b/monitoring/AbsLimitMonitor.h index d85d5b61..1206dbf6 100644 --- a/monitoring/AbsLimitMonitor.h +++ b/monitoring/AbsLimitMonitor.h @@ -8,12 +8,11 @@ template class AbsLimitMonitor: public MonitorBase { public: AbsLimitMonitor(object_id_t reporterId, uint8_t monitorId, - object_id_t dataCreatorId, lp_id_t localPoolId, - uint16_t confirmationLimit, T limit, + gp_id_t globalPoolId, uint16_t confirmationLimit, T limit, Event violationEvent = MonitoringIF::VALUE_OUT_OF_RANGE, bool aboveIsViolation = true) : - MonitorBase(reporterId, monitorId, dataCreatorId, localPoolId, - confirmationLimit), + MonitorBase(reporterId, monitorId, globalPoolId, + confirmationLimit), limit(limit), violationEvent(violationEvent), aboveIsViolation(aboveIsViolation) { } @@ -67,7 +66,8 @@ protected: switch (state) { case MonitoringIF::OUT_OF_RANGE: EventManagerIF::triggerEvent(this->reportingId, - violationEvent, this->parameterId); + violationEvent, this->parameterId.objectId, + this->parameterId.localPoolId); break; default: break; diff --git a/monitoring/LimitMonitor.h b/monitoring/LimitMonitor.h index fdb05018..27e35aa7 100644 --- a/monitoring/LimitMonitor.h +++ b/monitoring/LimitMonitor.h @@ -13,11 +13,11 @@ template class LimitMonitor: public MonitorBase { public: LimitMonitor(object_id_t reporterId, uint8_t monitorId, - object_id_t creatorId, lp_id_t localPoolId, - uint16_t confirmationLimit, T lowerLimit, T upperLimit, - Event belowLowEvent = MonitoringIF::VALUE_BELOW_LOW_LIMIT, + gp_id_t globalPoolId, uint16_t confirmationLimit, T lowerLimit, + T upperLimit, Event belowLowEvent = + MonitoringIF::VALUE_BELOW_LOW_LIMIT, Event aboveHighEvent = MonitoringIF::VALUE_ABOVE_HIGH_LIMIT) : - MonitorBase(reporterId, monitorId, creatorId, localPoolId, + MonitorBase(reporterId, monitorId, globalPoolId, confirmationLimit), lowerLimit(lowerLimit), upperLimit(upperLimit), belowLowEvent(belowLowEvent), aboveHighEvent(aboveHighEvent) { @@ -80,11 +80,11 @@ protected: switch (state) { case MonitoringIF::BELOW_LOW_LIMIT: EventManagerIF::triggerEvent(this->reportingId, belowLowEvent, - this->parameterId); + this->parameterId.objectId, this->parameterId.localPoolId); break; case MonitoringIF::ABOVE_HIGH_LIMIT: EventManagerIF::triggerEvent(this->reportingId, aboveHighEvent, - this->parameterId); + this->parameterId.objectId, this->parameterId.localPoolId); break; default: break; diff --git a/monitoring/MonitorBase.h b/monitoring/MonitorBase.h index df6f1af8..530a3840 100644 --- a/monitoring/MonitorBase.h +++ b/monitoring/MonitorBase.h @@ -25,11 +25,10 @@ class MonitorBase: public MonitorReporter { public: MonitorBase(object_id_t reporterId, uint8_t monitorId, - object_id_t dataCreatorId, lp_id_t localPoolId, - uint16_t confirmationLimit): - MonitorReporter(reporterId, monitorId, dataCreatorId, + gp_id_t globalPoolId, uint16_t confirmationLimit): + MonitorReporter(reporterId, monitorId, globalPoolId, confirmationLimit), - poolVariable(dataCreatorId, localPoolId) { + poolVariable(globalPoolId) { } virtual ~MonitorBase() { diff --git a/monitoring/MonitorReporter.h b/monitoring/MonitorReporter.h index d33f34d5..d6d8c8ed 100644 --- a/monitoring/MonitorReporter.h +++ b/monitoring/MonitorReporter.h @@ -5,6 +5,7 @@ #include "MonitoringIF.h" #include "MonitoringMessageContent.h" +#include "../datapoollocal/locPoolDefinitions.h" #include "../events/EventManagerIF.h" #include "../parameters/HasParametersIF.h" @@ -18,7 +19,7 @@ public: // TODO: Adapt to use SID instead of parameter ID. MonitorReporter(object_id_t reportingId, uint8_t monitorId, - uint32_t parameterId, uint16_t confirmationLimit) : + gp_id_t globalPoolId, uint16_t confirmationLimit) : monitorId(monitorId), parameterId(parameterId), reportingId(reportingId), oldState(MonitoringIF::UNCHECKED), reportingEnabled(ENABLED), eventEnabled(ENABLED), currentCounter(0), @@ -95,7 +96,7 @@ public: protected: const uint8_t monitorId; - const uint32_t parameterId; + const gp_id_t parameterId; object_id_t reportingId; ReturnValue_t oldState; diff --git a/monitoring/MonitoringMessageContent.h b/monitoring/MonitoringMessageContent.h index c82506f3..44d32656 100644 --- a/monitoring/MonitoringMessageContent.h +++ b/monitoring/MonitoringMessageContent.h @@ -3,6 +3,7 @@ #include "HasMonitorsIF.h" #include "MonitoringIF.h" +#include "../datapoollocal/locPoolDefinitions.h" #include "../objectmanager/ObjectManagerIF.h" #include "../serialize/SerialBufferAdapter.h" #include "../serialize/SerialFixedArrayListAdapter.h" @@ -16,12 +17,17 @@ void setStaticFrameworkObjectIds(); } //PID(uint32_t), TYPE, LIMIT_ID, value,limitValue, previous, later, timestamp +/** + * @brief Does magic. + * @tparam T + */ template class MonitoringReportContent: public SerialLinkedListAdapter { friend void (Factory::setStaticFrameworkObjectIds)(); public: SerializeElement monitorId; - SerializeElement parameterId; + SerializeElement parameterObjectId; + SerializeElement localPoolId; SerializeElement parameterValue; SerializeElement limitValue; SerializeElement oldState; @@ -30,20 +36,23 @@ public: SerializeElement> timestampSerializer; TimeStamperIF* timeStamper; MonitoringReportContent() : - SerialLinkedListAdapter( - LinkedElement::Iterator(¶meterId)), monitorId(0), parameterId( - 0), parameterValue(0), limitValue(0), oldState(0), newState( - 0), rawTimestamp( { 0 }), timestampSerializer(rawTimestamp, + SerialLinkedListAdapter(¶meterObjectId), + monitorId(0), parameterObjectId(0), + localPoolId(0), parameterValue(0), + limitValue(0), oldState(0), newState(0), + rawTimestamp( { 0 }), timestampSerializer(rawTimestamp, sizeof(rawTimestamp)), timeStamper(NULL) { setAllNext(); } - MonitoringReportContent(uint32_t setPID, T value, T limitValue, + MonitoringReportContent(gp_id_t globalPoolId, T value, T limitValue, ReturnValue_t oldState, ReturnValue_t newState) : - SerialLinkedListAdapter( - LinkedElement::Iterator(¶meterId)), monitorId(0), parameterId( - setPID), parameterValue(value), limitValue(limitValue), oldState( - oldState), newState(newState), timestampSerializer(rawTimestamp, - sizeof(rawTimestamp)), timeStamper(NULL) { + SerialLinkedListAdapter(¶meterObjectId), + monitorId(0), parameterObjectId(globalPoolId.objectId), + localPoolId(globalPoolId.localPoolId), + parameterValue(value), limitValue(limitValue), + oldState(oldState), newState(newState), + timestampSerializer(rawTimestamp, sizeof(rawTimestamp)), + timeStamper(NULL) { setAllNext(); if (checkAndSetStamper()) { timeStamper->addTimeStamp(rawTimestamp, sizeof(rawTimestamp)); @@ -53,16 +62,16 @@ private: static object_id_t timeStamperId; void setAllNext() { - parameterId.setNext(¶meterValue); + parameterObjectId.setNext(¶meterValue); parameterValue.setNext(&limitValue); limitValue.setNext(&oldState); oldState.setNext(&newState); newState.setNext(×tampSerializer); } bool checkAndSetStamper() { - if (timeStamper == NULL) { + if (timeStamper == nullptr) { timeStamper = objectManager->get( timeStamperId ); - if ( timeStamper == NULL ) { + if ( timeStamper == nullptr ) { sif::error << "MonitoringReportContent::checkAndSetStamper: " "Stamper not found!" << std::endl; return false; diff --git a/power/Fuse.cpp b/power/Fuse.cpp index ace625bf..ff9c7b3c 100644 --- a/power/Fuse.cpp +++ b/power/Fuse.cpp @@ -8,17 +8,16 @@ object_id_t Fuse::powerSwitchId = 0; -Fuse::Fuse(object_id_t fuseObjectId, uint8_t fuseId, VariableIds ids, +Fuse::Fuse(object_id_t fuseObjectId, uint8_t fuseId, + sid_t variableSet, VariableIds ids, float maxCurrent, uint16_t confirmationCount) : - SystemObject(fuseObjectId), oldFuseState(0), fuseId(fuseId), powerIF( - NULL), + SystemObject(fuseObjectId), oldFuseState(0), fuseId(fuseId), currentLimit(fuseObjectId, 1, ids.pidCurrent, confirmationCount, maxCurrent, FUSE_CURRENT_HIGH), - powerMonitor(fuseObjectId, 2, - GlobalDataPool::poolIdAndPositionToPid(ids.poolIdPower, 0), + powerMonitor(fuseObjectId, 2, ids.poolIdPower, confirmationCount), - set(), voltage(ids.pidVoltage, &set), current(ids.pidCurrent, &set), - state(ids.pidState, &set), + set(variableSet), voltage(ids.pidVoltage, &set), + current(ids.pidCurrent, &set), state(ids.pidState, &set), power(ids.poolIdPower, &set, PoolVariableIF::VAR_READ_WRITE), parameterHelper(this), healthHelper(this, fuseObjectId) { commandQueue = QueueFactory::instance()->createMessageQueue(); @@ -73,19 +72,19 @@ ReturnValue_t Fuse::check() { checkFuseState(); calculateFusePower(); //Check if power is valid and if fuse state is off or invalid. - if (!power.isValid() || (state == 0) || !state.isValid()) { - result = powerMonitor.setToInvalid(); - } else { - float lowLimit = 0.0; - float highLimit = RESIDUAL_POWER; - calculatePowerLimits(&lowLimit, &highLimit); - result = powerMonitor.checkPower(power, lowLimit, highLimit); - if (result == MonitoringIF::BELOW_LOW_LIMIT) { - reportEvents(POWER_BELOW_LOW_LIMIT); - } else if (result == MonitoringIF::ABOVE_HIGH_LIMIT) { - reportEvents(POWER_ABOVE_HIGH_LIMIT); - } - } +// if (!power.isValid() || (state == 0) || !state.isValid()) { +// result = powerMonitor.setToInvalid(); +// } else { +// float lowLimit = 0.0; +// float highLimit = RESIDUAL_POWER; +// calculatePowerLimits(&lowLimit, &highLimit); +// result = powerMonitor.checkPower(power, lowLimit, highLimit); +// if (result == MonitoringIF::BELOW_LOW_LIMIT) { +// reportEvents(POWER_BELOW_LOW_LIMIT); +// } else if (result == MonitoringIF::ABOVE_HIGH_LIMIT) { +// reportEvents(POWER_ABOVE_HIGH_LIMIT); +// } +// } set.commit(); return result; } @@ -136,7 +135,7 @@ void Fuse::calculateFusePower() { return; } //Calculate fuse power. - power = current * voltage; + //power = current * voltage; power.setValid(PoolVariableIF::VALID); } @@ -189,20 +188,20 @@ void Fuse::checkFuseState() { oldFuseState = 0; return; } - if (state == 0) { - if (oldFuseState != 0) { - reportEvents(FUSE_WENT_OFF); - } - } - oldFuseState = state; +// if (state == 0) { +// if (oldFuseState != 0) { +// reportEvents(FUSE_WENT_OFF); +// } +// } +// oldFuseState = state; } float Fuse::getPower() { - if (power.isValid()) { - return power; - } else { - return 0.0; - } +// if (power.isValid()) { +// return power; +// } else { +// return 0.0; +// } } void Fuse::setDataPoolEntriesInvalid() { diff --git a/power/Fuse.h b/power/Fuse.h index 13a0eb7c..5e54d294 100644 --- a/power/Fuse.h +++ b/power/Fuse.h @@ -13,6 +13,7 @@ #include "../parameters/ParameterHelper.h" #include +#include "../datapoollocal/StaticLocalDataSet.h" namespace Factory { void setStaticFrameworkObjectIds(); } @@ -27,10 +28,10 @@ private: static constexpr float RESIDUAL_POWER = 0.005 * 28.5; //!< This is the upper limit of residual power lost by fuses and switches. Worst case is Fuse and one of two switches on. See PCDU ICD 1.9 p29 bottom public: struct VariableIds { - uint32_t pidVoltage; - uint32_t pidCurrent; - uint32_t pidState; - uint32_t poolIdPower; + gp_id_t pidVoltage; + gp_id_t pidCurrent; + gp_id_t pidState; + gp_id_t poolIdPower; }; static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PCDU_1; @@ -40,8 +41,8 @@ public: static const Event POWER_BELOW_LOW_LIMIT = MAKE_EVENT(5, SEVERITY::LOW); //!< PSS detected a fuse that violates its limits. typedef std::list DeviceList; - Fuse(object_id_t fuseObjectId, uint8_t fuseId, VariableIds ids, - float maxCurrent, uint16_t confirmationCount = 2); + Fuse(object_id_t fuseObjectId, uint8_t fuseId, sid_t variableSet, + VariableIds ids, float maxCurrent, uint16_t confirmationCount = 2); virtual ~Fuse(); void addDevice(PowerComponentIF *set); float getPower(); @@ -71,12 +72,12 @@ public: private: uint8_t oldFuseState; uint8_t fuseId; - PowerSwitchIF *powerIF; //could be static in our case. + PowerSwitchIF *powerIF = nullptr; //could be static in our case. AbsLimitMonitor currentLimit; class PowerMonitor: public MonitorReporter { public: template - PowerMonitor(Args ... args) : + PowerMonitor(Args ... args): MonitorReporter(std::forward(args)...) { } ReturnValue_t checkPower(float sample, float lowerLimit, @@ -86,11 +87,16 @@ private: }; PowerMonitor powerMonitor; - GlobDataSet set; - PIDReader voltage; - PIDReader current; - PIDReader state; - gp_float_t power; + StaticLocalDataSet<3> set; + //LocalPoolDataSetBase* set = nullptr; + //PIDReader voltage; + //PIDReader current; + //PIDReader state; + lp_var_t voltage; + lp_var_t current; + lp_var_t state; + + lp_var_t power; MessageQueueIF* commandQueue = nullptr; ParameterHelper parameterHelper; HealthHelper healthHelper; diff --git a/power/PowerSensor.cpp b/power/PowerSensor.cpp index 739d02f2..8fe3cf3a 100644 --- a/power/PowerSensor.cpp +++ b/power/PowerSensor.cpp @@ -2,15 +2,17 @@ #include "../ipc/QueueFactory.h" -PowerSensor::PowerSensor(object_id_t setId, VariableIds ids, +PowerSensor::PowerSensor(object_id_t objectId, sid_t setId, VariableIds ids, DefaultLimits limits, SensorEvents events, uint16_t confirmationCount) : - SystemObject(setId), parameterHelper(this), healthHelper(this, setId), - set(), current(ids.pidCurrent, &set), voltage(ids.pidVoltage, &set), - power(ids.poolIdPower, &set, PoolVariableIF::VAR_WRITE), - currentLimit(setId, MODULE_ID_CURRENT, ids.pidCurrent, confirmationCount, + SystemObject(objectId), parameterHelper(this), + healthHelper(this, objectId), + powerSensorSet(setId), current(ids.pidCurrent, &powerSensorSet), + voltage(ids.pidVoltage, &powerSensorSet), + power(ids.poolIdPower, &powerSensorSet, PoolVariableIF::VAR_WRITE), + currentLimit(objectId, MODULE_ID_CURRENT, ids.pidCurrent, confirmationCount, limits.currentMin, limits.currentMax, events.currentLow, events.currentHigh), - voltageLimit(setId, MODULE_ID_VOLTAGE, + voltageLimit(objectId, MODULE_ID_VOLTAGE, ids.pidVoltage, confirmationCount, limits.voltageMin, limits.voltageMax, events.voltageLow, events.voltageHigh) { commandQueue = QueueFactory::instance()->createMessageQueue(); @@ -21,27 +23,27 @@ PowerSensor::~PowerSensor() { } ReturnValue_t PowerSensor::calculatePower() { - set.read(); + powerSensorSet.read(); ReturnValue_t result1 = HasReturnvaluesIF::RETURN_FAILED; ReturnValue_t result2 = HasReturnvaluesIF::RETURN_FAILED; - if (healthHelper.healthTable->isHealthy(getObjectId()) && voltage.isValid() - && current.isValid()) { - result1 = voltageLimit.doCheck(voltage); - result2 = currentLimit.doCheck(current); - } else { - voltageLimit.setToInvalid(); - currentLimit.setToInvalid(); - result1 = OBJECT_NOT_HEALTHY; - } - if (result1 != HasReturnvaluesIF::RETURN_OK - || result2 != HasReturnvaluesIF::RETURN_OK) { - result1 = MonitoringIF::INVALID; - power.setValid(PoolVariableIF::INVALID); - } else { - power.setValid(PoolVariableIF::VALID); - power = current * voltage; - } - set.commit(); +// if (healthHelper.healthTable->isHealthy(getObjectId()) && voltage.isValid() +// && current.isValid()) { +// result1 = voltageLimit.doCheck(voltage); +// result2 = currentLimit.doCheck(current); +// } else { +// voltageLimit.setToInvalid(); +// currentLimit.setToInvalid(); +// result1 = OBJECT_NOT_HEALTHY; +// } +// if (result1 != HasReturnvaluesIF::RETURN_OK +// || result2 != HasReturnvaluesIF::RETURN_OK) { +// result1 = MonitoringIF::INVALID; +// power.setValid(PoolVariableIF::INVALID); +// } else { +// power.setValid(PoolVariableIF::VALID); +// power = current * voltage; +// } +// powerSensorSet.commit(); return result1; } @@ -94,8 +96,8 @@ void PowerSensor::checkCommandQueue() { } void PowerSensor::setDataPoolEntriesInvalid() { - set.read(); - set.commit(PoolVariableIF::INVALID); + powerSensorSet.read(); + powerSensorSet.commit(PoolVariableIF::INVALID); } float PowerSensor::getPower() { diff --git a/power/PowerSensor.h b/power/PowerSensor.h index 20ebff39..bb409f3e 100644 --- a/power/PowerSensor.h +++ b/power/PowerSensor.h @@ -4,6 +4,7 @@ #include "../datapoolglob/GlobalDataSet.h" #include "../datapoolglob/GlobalPoolVariable.h" #include "../datapoolglob/PIDReader.h" +#include "../datapoollocal/StaticLocalDataSet.h" #include "../devicehandlers/HealthDevice.h" #include "../monitoring/LimitMonitor.h" #include "../parameters/ParameterHelper.h" @@ -12,15 +13,18 @@ class PowerController; +/** + * @brief Does magic. + */ class PowerSensor: public SystemObject, public ReceivesParameterMessagesIF, public HasHealthIF { friend class PowerController; public: struct VariableIds { - uint32_t pidCurrent; - uint32_t pidVoltage; - uint32_t poolIdPower; + gp_id_t pidCurrent; + gp_id_t pidVoltage; + gp_id_t poolIdPower; }; struct DefaultLimits { float currentMin; @@ -34,8 +38,9 @@ public: Event voltageLow; Event voltageHigh; }; - PowerSensor(object_id_t setId, VariableIds setIds, DefaultLimits limits, - SensorEvents events, uint16_t confirmationCount = 0); + PowerSensor(object_id_t objectId, sid_t sid, VariableIds setIds, + DefaultLimits limits, SensorEvents events, + uint16_t confirmationCount = 0); virtual ~PowerSensor(); ReturnValue_t calculatePower(); ReturnValue_t performOperation(uint8_t opCode); @@ -53,12 +58,16 @@ private: MessageQueueIF* commandQueue = nullptr; ParameterHelper parameterHelper; HealthHelper healthHelper; - GlobDataSet set; + //GlobDataSet set; + StaticLocalDataSet<3> powerSensorSet; //Variables in - PIDReader current; - PIDReader voltage; + lp_var_t current; + lp_var_t voltage; + //PIDReader current; + //PIDReader voltage; //Variables out - gp_float_t power; + lp_var_t power; + //gp_float_t power; static const uint8_t MODULE_ID_CURRENT = 1; static const uint8_t MODULE_ID_VOLTAGE = 2; diff --git a/thermal/ThermalMonitorReporter.cpp b/thermal/ThermalMonitorReporter.cpp index 766ea1cb..cefc6110 100644 --- a/thermal/ThermalMonitorReporter.cpp +++ b/thermal/ThermalMonitorReporter.cpp @@ -38,7 +38,8 @@ bool ThermalMonitorReporter::isAboveHighLimit() { } } -ReturnValue_t ThermalMonitorReporter::translateState(ThermalComponentIF::State state, float sample, float lowerLimit, +ReturnValue_t ThermalMonitorReporter::translateState( + ThermalComponentIF::State state, float sample, float lowerLimit, float upperLimit, bool componentIsOperational) { if (ThermalComponentIF::isIgnoredState(state)) { setToUnchecked(); @@ -46,10 +47,12 @@ ReturnValue_t ThermalMonitorReporter::translateState(ThermalComponentIF::State s } switch (state) { case ThermalComponentIF::OUT_OF_RANGE_LOW: - return monitorStateIs(MonitoringIF::BELOW_LOW_LIMIT, sample, lowerLimit); + return monitorStateIs(MonitoringIF::BELOW_LOW_LIMIT, sample, + lowerLimit); case ThermalComponentIF::NON_OPERATIONAL_LOW: if (componentIsOperational) { - return monitorStateIs(ThermalComponentIF::BELOW_OPERATIONAL_LIMIT, sample, lowerLimit); + return monitorStateIs(ThermalComponentIF::BELOW_OPERATIONAL_LIMIT, + sample, lowerLimit); } else { return monitorStateIs(HasReturnvaluesIF::RETURN_OK, sample, 0.0); } @@ -57,12 +60,14 @@ ReturnValue_t ThermalMonitorReporter::translateState(ThermalComponentIF::State s return monitorStateIs(HasReturnvaluesIF::RETURN_OK, sample, 0.0); case ThermalComponentIF::NON_OPERATIONAL_HIGH: if (componentIsOperational) { - return monitorStateIs(ThermalComponentIF::ABOVE_OPERATIONAL_LIMIT, sample, upperLimit); + return monitorStateIs(ThermalComponentIF::ABOVE_OPERATIONAL_LIMIT, + sample, upperLimit); } else { return monitorStateIs(HasReturnvaluesIF::RETURN_OK, sample, 0.0); } case ThermalComponentIF::OUT_OF_RANGE_HIGH: - return monitorStateIs(MonitoringIF::ABOVE_HIGH_LIMIT, sample, upperLimit); + return monitorStateIs(MonitoringIF::ABOVE_HIGH_LIMIT, sample, + upperLimit); default: //Never reached, all states covered. return HasReturnvaluesIF::RETURN_FAILED; From 4bb9dd816ec910870cd8183951efbdae9cb74d0d Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 16:06:59 +0100 Subject: [PATCH 07/22] compiling again --- datapoollocal/LocalPoolObjectBase.cpp | 4 + datapoollocal/LocalPoolObjectBase.h | 1 + datapoollocal/LocalPoolVariable.h | 18 +++- datapoollocal/LocalPoolVariable.tpp | 49 ++++++++- monitoring/AbsLimitMonitor.h | 4 +- monitoring/LimitMonitor.h | 4 +- monitoring/MonitorReporter.h | 8 +- power/Fuse.cpp | 50 ++++----- power/PowerSensor.cpp | 36 +++---- thermal/ThermalComponentCore.cpp | 146 +++++++++++++------------- thermal/ThermalComponentCore.h | 6 +- thermal/tcsDefinitions.h | 2 +- 12 files changed, 196 insertions(+), 132 deletions(-) diff --git a/datapoollocal/LocalPoolObjectBase.cpp b/datapoollocal/LocalPoolObjectBase.cpp index c25df25a..b4d0e306 100644 --- a/datapoollocal/LocalPoolObjectBase.cpp +++ b/datapoollocal/LocalPoolObjectBase.cpp @@ -67,3 +67,7 @@ void LocalPoolObjectBase::setChanged(bool changed) { bool LocalPoolObjectBase::hasChanged() const { return changed; } + +void LocalPoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) { + this->readWriteMode = newReadWriteMode; +} diff --git a/datapoollocal/LocalPoolObjectBase.h b/datapoollocal/LocalPoolObjectBase.h index 8d1ab45e..7165fc24 100644 --- a/datapoollocal/LocalPoolObjectBase.h +++ b/datapoollocal/LocalPoolObjectBase.h @@ -18,6 +18,7 @@ public: DataSetIF* dataSet = nullptr, pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + void setReadWriteMode(pool_rwm_t newReadWriteMode); pool_rwm_t getReadWriteMode() const; bool isValid() const override; diff --git a/datapoollocal/LocalPoolVariable.h b/datapoollocal/LocalPoolVariable.h index 173c2035..bb36089d 100644 --- a/datapoollocal/LocalPoolVariable.h +++ b/datapoollocal/LocalPoolVariable.h @@ -118,8 +118,21 @@ public: ReturnValue_t commit(dur_millis_t lockTimeout = MutexIF::BLOCKING) override; - LocalPoolVar &operator=(T newValue); - LocalPoolVar &operator=(LocalPoolVar newPoolVariable); + LocalPoolVar &operator=(const T& newValue); + LocalPoolVar &operator=(const LocalPoolVar& newPoolVariable); + + bool operator==(const LocalPoolVar& other) const; + bool operator==(const T& other) const; + + bool operator!=(const LocalPoolVar& other) const; + bool operator!=(const T& other) const; + + bool operator<(const LocalPoolVar& other) const; + bool operator<(const T& other) const; + + bool operator>(const LocalPoolVar& other) const; + bool operator>(const T& other) const; + protected: /** * @brief Like #read, but without a lock protection of the global pool. @@ -165,5 +178,4 @@ using lp_int64_t = LocalPoolVar; using lp_float_t = LocalPoolVar; using lp_double_t = LocalPoolVar; - #endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ */ diff --git a/datapoollocal/LocalPoolVariable.tpp b/datapoollocal/LocalPoolVariable.tpp index 8aa164d1..a63e550d 100644 --- a/datapoollocal/LocalPoolVariable.tpp +++ b/datapoollocal/LocalPoolVariable.tpp @@ -81,14 +81,14 @@ inline ReturnValue_t LocalPoolVar::commitWithoutLock() { } template -inline LocalPoolVar & LocalPoolVar::operator=(T newValue) { +inline LocalPoolVar & LocalPoolVar::operator=(const T& newValue) { value = newValue; return *this; } template inline LocalPoolVar& LocalPoolVar::operator =( - LocalPoolVar newPoolVariable) { + const LocalPoolVar& newPoolVariable) { value = newPoolVariable.value; return *this; } @@ -119,4 +119,47 @@ inline std::ostream& operator<< (std::ostream &out, return out; } -#endif +template +inline bool LocalPoolVar::operator ==(const LocalPoolVar &other) const { + return this->value == other.value; +} + +template +inline bool LocalPoolVar::operator ==(const T &other) const { + return this->value == other; +} + + +template +inline bool LocalPoolVar::operator !=(const LocalPoolVar &other) const { + return not (*this == other); +} + +template +inline bool LocalPoolVar::operator !=(const T &other) const { + return not (*this == other); +} + + +template +inline bool LocalPoolVar::operator <(const LocalPoolVar &other) const { + return this->value < other.value; +} + +template +inline bool LocalPoolVar::operator <(const T &other) const { + return this->value < other; +} + + +template +inline bool LocalPoolVar::operator >(const LocalPoolVar &other) const { + return not (*this < other); +} + +template +inline bool LocalPoolVar::operator >(const T &other) const { + return not (*this < other); +} + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_ */ diff --git a/monitoring/AbsLimitMonitor.h b/monitoring/AbsLimitMonitor.h index 1206dbf6..5feb369c 100644 --- a/monitoring/AbsLimitMonitor.h +++ b/monitoring/AbsLimitMonitor.h @@ -66,8 +66,8 @@ protected: switch (state) { case MonitoringIF::OUT_OF_RANGE: EventManagerIF::triggerEvent(this->reportingId, - violationEvent, this->parameterId.objectId, - this->parameterId.localPoolId); + violationEvent, this->globalPoolId.objectId, + this->globalPoolId.localPoolId); break; default: break; diff --git a/monitoring/LimitMonitor.h b/monitoring/LimitMonitor.h index 27e35aa7..c4448ced 100644 --- a/monitoring/LimitMonitor.h +++ b/monitoring/LimitMonitor.h @@ -80,11 +80,11 @@ protected: switch (state) { case MonitoringIF::BELOW_LOW_LIMIT: EventManagerIF::triggerEvent(this->reportingId, belowLowEvent, - this->parameterId.objectId, this->parameterId.localPoolId); + this->globalPoolId.objectId, this->globalPoolId.localPoolId); break; case MonitoringIF::ABOVE_HIGH_LIMIT: EventManagerIF::triggerEvent(this->reportingId, aboveHighEvent, - this->parameterId.objectId, this->parameterId.localPoolId); + this->globalPoolId.objectId, this->globalPoolId.localPoolId); break; default: break; diff --git a/monitoring/MonitorReporter.h b/monitoring/MonitorReporter.h index d6d8c8ed..9028e7e4 100644 --- a/monitoring/MonitorReporter.h +++ b/monitoring/MonitorReporter.h @@ -20,7 +20,7 @@ public: MonitorReporter(object_id_t reportingId, uint8_t monitorId, gp_id_t globalPoolId, uint16_t confirmationLimit) : - monitorId(monitorId), parameterId(parameterId), + monitorId(monitorId), globalPoolId(globalPoolId), reportingId(reportingId), oldState(MonitoringIF::UNCHECKED), reportingEnabled(ENABLED), eventEnabled(ENABLED), currentCounter(0), confirmationLimit(confirmationLimit) { @@ -96,7 +96,7 @@ public: protected: const uint8_t monitorId; - const gp_id_t parameterId; + const gp_id_t globalPoolId; object_id_t reportingId; ReturnValue_t oldState; @@ -167,13 +167,13 @@ protected: */ virtual void sendTransitionReport(T parameterValue, T crossedLimit, ReturnValue_t state) { - MonitoringReportContent report(parameterId, + MonitoringReportContent report(globalPoolId, parameterValue, crossedLimit, oldState, state); LimitViolationReporter::sendLimitViolationReport(&report); } ReturnValue_t setToState(ReturnValue_t state) { if (oldState != state && reportingEnabled) { - MonitoringReportContent report(parameterId, 0, 0, oldState, + MonitoringReportContent report(globalPoolId, 0, 0, oldState, state); LimitViolationReporter::sendLimitViolationReport(&report); oldState = state; diff --git a/power/Fuse.cpp b/power/Fuse.cpp index ff9c7b3c..1c30d83f 100644 --- a/power/Fuse.cpp +++ b/power/Fuse.cpp @@ -72,19 +72,19 @@ ReturnValue_t Fuse::check() { checkFuseState(); calculateFusePower(); //Check if power is valid and if fuse state is off or invalid. -// if (!power.isValid() || (state == 0) || !state.isValid()) { -// result = powerMonitor.setToInvalid(); -// } else { -// float lowLimit = 0.0; -// float highLimit = RESIDUAL_POWER; -// calculatePowerLimits(&lowLimit, &highLimit); -// result = powerMonitor.checkPower(power, lowLimit, highLimit); -// if (result == MonitoringIF::BELOW_LOW_LIMIT) { -// reportEvents(POWER_BELOW_LOW_LIMIT); -// } else if (result == MonitoringIF::ABOVE_HIGH_LIMIT) { -// reportEvents(POWER_ABOVE_HIGH_LIMIT); -// } -// } + if (!power.isValid() || (state == 0) || !state.isValid()) { + result = powerMonitor.setToInvalid(); + } else { + float lowLimit = 0.0; + float highLimit = RESIDUAL_POWER; + calculatePowerLimits(&lowLimit, &highLimit); + result = powerMonitor.checkPower(power.value, lowLimit, highLimit); + if (result == MonitoringIF::BELOW_LOW_LIMIT) { + reportEvents(POWER_BELOW_LOW_LIMIT); + } else if (result == MonitoringIF::ABOVE_HIGH_LIMIT) { + reportEvents(POWER_ABOVE_HIGH_LIMIT); + } + } set.commit(); return result; } @@ -135,7 +135,7 @@ void Fuse::calculateFusePower() { return; } //Calculate fuse power. - //power = current * voltage; + power.value = current.value * voltage.value; power.setValid(PoolVariableIF::VALID); } @@ -188,20 +188,20 @@ void Fuse::checkFuseState() { oldFuseState = 0; return; } -// if (state == 0) { -// if (oldFuseState != 0) { -// reportEvents(FUSE_WENT_OFF); -// } -// } -// oldFuseState = state; + if (state == 0) { + if (oldFuseState != 0) { + reportEvents(FUSE_WENT_OFF); + } + } + oldFuseState = state.value; } float Fuse::getPower() { -// if (power.isValid()) { -// return power; -// } else { -// return 0.0; -// } + if (power.isValid()) { + return power.value; + } else { + return 0.0; + } } void Fuse::setDataPoolEntriesInvalid() { diff --git a/power/PowerSensor.cpp b/power/PowerSensor.cpp index 8fe3cf3a..1ef041e3 100644 --- a/power/PowerSensor.cpp +++ b/power/PowerSensor.cpp @@ -26,24 +26,24 @@ ReturnValue_t PowerSensor::calculatePower() { powerSensorSet.read(); ReturnValue_t result1 = HasReturnvaluesIF::RETURN_FAILED; ReturnValue_t result2 = HasReturnvaluesIF::RETURN_FAILED; -// if (healthHelper.healthTable->isHealthy(getObjectId()) && voltage.isValid() -// && current.isValid()) { -// result1 = voltageLimit.doCheck(voltage); -// result2 = currentLimit.doCheck(current); -// } else { -// voltageLimit.setToInvalid(); -// currentLimit.setToInvalid(); -// result1 = OBJECT_NOT_HEALTHY; -// } -// if (result1 != HasReturnvaluesIF::RETURN_OK -// || result2 != HasReturnvaluesIF::RETURN_OK) { -// result1 = MonitoringIF::INVALID; -// power.setValid(PoolVariableIF::INVALID); -// } else { -// power.setValid(PoolVariableIF::VALID); -// power = current * voltage; -// } -// powerSensorSet.commit(); + if (healthHelper.healthTable->isHealthy(getObjectId()) && voltage.isValid() + && current.isValid()) { + result1 = voltageLimit.doCheck(voltage.value); + result2 = currentLimit.doCheck(current.value); + } else { + voltageLimit.setToInvalid(); + currentLimit.setToInvalid(); + result1 = OBJECT_NOT_HEALTHY; + } + if (result1 != HasReturnvaluesIF::RETURN_OK + || result2 != HasReturnvaluesIF::RETURN_OK) { + result1 = MonitoringIF::INVALID; + power.setValid(PoolVariableIF::INVALID); + } else { + power.setValid(PoolVariableIF::VALID); + power.value = current.value * voltage.value; + } + powerSensorSet.commit(); return result1; } diff --git a/thermal/ThermalComponentCore.cpp b/thermal/ThermalComponentCore.cpp index 53eeb58f..a64491c0 100644 --- a/thermal/ThermalComponentCore.cpp +++ b/thermal/ThermalComponentCore.cpp @@ -9,16 +9,18 @@ ThermalComponentCore::ThermalComponentCore(object_id_t reportingObjectId, targetState(targetStatePoolId, dataSet, PoolVariableIF::VAR_READ), currentState(currentStatePoolId, dataSet, PoolVariableIF::VAR_WRITE), heaterRequest(requestPoolId, dataSet, PoolVariableIF::VAR_WRITE), - parameters(parameters), domainId(domainId) { - //temperatureMonitor(reportingObjectId, domainId + 1, - // GlobalDataPool::poolIdAndPositionToPid(temperaturePoolId, 0), - // COMPONENT_TEMP_CONFIRMATION), domainId(domainId) { + parameters(parameters), domainId(domainId), + temperatureMonitor(reportingObjectId, domainId + 1,temperaturePoolId, + COMPONENT_TEMP_CONFIRMATION) { //Set thermal state once, then leave to operator. - //GlobDataSet mySet; - //gp_uint8_t writableTargetState(targetStatePoolId, &mySet, - // PoolVariableIF::VAR_WRITE); - //writableTargetState = initialTargetState; - //mySet.commit(PoolVariableIF::VALID); + targetState.setReadWriteMode(PoolVariableIF::VAR_WRITE); + ReturnValue_t result = targetState.read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + targetState = initialTargetState; + targetState.setValid(true); + targetState.commit(); + } + targetState.setReadWriteMode(PoolVariableIF::VAR_READ); } void ThermalComponentCore::addSensor(AbstractTemperatureSensor* sensor) { @@ -57,19 +59,21 @@ ThermalComponentIF::HeaterRequest ThermalComponentCore::performOperation( HeaterRequest request = HEATER_DONT_CARE; //SHOULDDO: Better pass db_float_t* to getTemperature and set it invalid if invalid. temperature = getTemperature(); - //updateMinMaxTemp(); - //if ((temperature != INVALID_TEMPERATURE)) { - //temperature.setValid(PoolVariableIF::VALID); - //State state = getState(temperature, getParameters(), targetState); - //currentState = state; - //checkLimits(state); - //request = getHeaterRequest(targetState, temperature, getParameters()); - //} else { - // temperatureMonitor.setToInvalid(); - // temperature.setValid(PoolVariableIF::INVALID); - // currentState = UNKNOWN; - // request = HEATER_DONT_CARE; - //} + updateMinMaxTemp(); + if ((temperature != INVALID_TEMPERATURE)) { + temperature.setValid(PoolVariableIF::VALID); + State state = getState(temperature.value, getParameters(), + targetState.value); + currentState = state; + checkLimits(state); + request = getHeaterRequest(targetState.value, temperature.value, + getParameters()); + } else { + temperatureMonitor.setToInvalid(); + temperature.setValid(PoolVariableIF::INVALID); + currentState = UNKNOWN; + request = HEATER_DONT_CARE; + } currentState.setValid(PoolVariableIF::VALID); heaterRequest = request; heaterRequest.setValid(PoolVariableIF::VALID); @@ -77,11 +81,11 @@ ThermalComponentIF::HeaterRequest ThermalComponentCore::performOperation( } void ThermalComponentCore::markStateIgnored() { - //currentState = getIgnoredState(currentState); + currentState = getIgnoredState(currentState.value); } object_id_t ThermalComponentCore::getObjectId() { - //return temperatureMonitor.getReporterId(); + return temperatureMonitor.getReporterId(); return 0; } @@ -120,7 +124,7 @@ void ThermalComponentCore::setOutputInvalid() { currentState.setValid(PoolVariableIF::INVALID); heaterRequest = HEATER_DONT_CARE; heaterRequest.setValid(PoolVariableIF::INVALID); - //temperatureMonitor.setToUnchecked(); + temperatureMonitor.setToUnchecked(); } float ThermalComponentCore::getTemperature() { @@ -170,8 +174,8 @@ ThermalComponentIF::State ThermalComponentCore::getState(float temperature, void ThermalComponentCore::checkLimits(ThermalComponentIF::State state) { //Checks operational limits only. - //temperatureMonitor.translateState(state, temperature.value, - // getParameters().lowerOpLimit, getParameters().upperOpLimit); + temperatureMonitor.translateState(state, temperature.value, + getParameters().lowerOpLimit, getParameters().upperOpLimit); } @@ -221,17 +225,17 @@ ThermalComponentIF::State ThermalComponentCore::getIgnoredState(int8_t state) { } } -//void ThermalComponentCore::updateMinMaxTemp() { -// if (temperature == INVALID_TEMPERATURE) { -// return; -// } -// if (temperature < minTemp) { -// minTemp = temperature; -// } -// if (temperature > maxTemp) { -// maxTemp = temperature; -// } -//} +void ThermalComponentCore::updateMinMaxTemp() { + if (temperature == INVALID_TEMPERATURE) { + return; + } + if (temperature < minTemp) { + minTemp = temperature.value; + } + if (temperature > maxTemp) { + maxTemp = temperature.value; + } +} uint8_t ThermalComponentCore::getDomainId() const { return domainId; @@ -244,38 +248,38 @@ ThermalComponentCore::Parameters ThermalComponentCore::getParameters() { ReturnValue_t ThermalComponentCore::getParameter(uint8_t domainId, uint16_t parameterId, ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues, uint16_t startAtIndex) { - //ReturnValue_t result = temperatureMonitor.getParameter(domainId, - // parameterId, parameterWrapper, newValues, startAtIndex); -// if (result != INVALID_DOMAIN_ID) { -// return result; -// } -// if (domainId != this->domainId) { -// return INVALID_DOMAIN_ID; -// } -// switch (parameterId) { -// case 0: -// parameterWrapper->set(parameters.heaterOn); -// break; -// case 1: -// parameterWrapper->set(parameters.hysteresis); -// break; -// case 2: -// parameterWrapper->set(parameters.heaterSwitchoff); -// break; -// case 3: -// parameterWrapper->set(minTemp); -// break; -// case 4: -// parameterWrapper->set(maxTemp); -// break; -// case 10: -// parameterWrapper->set(parameters.lowerOpLimit); -// break; -// case 11: -// parameterWrapper->set(parameters.upperOpLimit); -// break; -// default: -// return INVALID_IDENTIFIER_ID; -// } + ReturnValue_t result = temperatureMonitor.getParameter(domainId, + parameterId, parameterWrapper, newValues, startAtIndex); + if (result != INVALID_DOMAIN_ID) { + return result; + } + if (domainId != this->domainId) { + return INVALID_DOMAIN_ID; + } + switch (parameterId) { + case 0: + parameterWrapper->set(parameters.heaterOn); + break; + case 1: + parameterWrapper->set(parameters.hysteresis); + break; + case 2: + parameterWrapper->set(parameters.heaterSwitchoff); + break; + case 3: + parameterWrapper->set(minTemp); + break; + case 4: + parameterWrapper->set(maxTemp); + break; + case 10: + parameterWrapper->set(parameters.lowerOpLimit); + break; + case 11: + parameterWrapper->set(parameters.upperOpLimit); + break; + default: + return INVALID_IDENTIFIER_ID; + } return HasReturnvaluesIF::RETURN_OK; } diff --git a/thermal/ThermalComponentCore.h b/thermal/ThermalComponentCore.h index 6ef59058..19d72359 100644 --- a/thermal/ThermalComponentCore.h +++ b/thermal/ThermalComponentCore.h @@ -96,10 +96,10 @@ protected: Parameters parameters; - //ThermalMonitorReporter temperatureMonitor; - const uint8_t domainId; + ThermalMonitorReporter temperatureMonitor; + virtual float getTemperature(); virtual State getState(float temperature, Parameters parameters, int8_t targetState); @@ -111,7 +111,7 @@ protected: virtual State getIgnoredState(int8_t state); - //void updateMinMaxTemp(); + void updateMinMaxTemp(); virtual Parameters getParameters(); }; diff --git a/thermal/tcsDefinitions.h b/thermal/tcsDefinitions.h index ad258ced..37e5b849 100644 --- a/thermal/tcsDefinitions.h +++ b/thermal/tcsDefinitions.h @@ -2,7 +2,7 @@ #define TCSDEFINITIONS_H_ -static const uint32_t INVALID_TEMPERATURE = 999; +static const float INVALID_TEMPERATURE = 999; #endif /* TCSDEFINITIONS_H_ */ From f581b37fcf51415fe9f24ff22bb61aaf367739f8 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 16:25:09 +0100 Subject: [PATCH 08/22] thermal module replacements --- power/Fuse.h | 3 -- power/PowerSensor.h | 3 -- thermal/ThermalComponent.cpp | 33 +++++++------ thermal/ThermalComponentCore.cpp | 39 +++++++-------- thermal/ThermalComponentCore.h | 2 - thermal/ThermalModule.cpp | 84 +++++++++++++++++--------------- thermal/ThermalModule.h | 46 +++++++++-------- 7 files changed, 109 insertions(+), 101 deletions(-) diff --git a/power/Fuse.h b/power/Fuse.h index 5e54d294..e9e9290b 100644 --- a/power/Fuse.h +++ b/power/Fuse.h @@ -4,9 +4,6 @@ #include "PowerComponentIF.h" #include "PowerSwitchIF.h" -#include "../datapoolglob/GlobalDataSet.h" -#include "../datapoolglob/GlobalPoolVariable.h" -#include "../datapoolglob/PIDReader.h" #include "../devicehandlers/HealthDevice.h" #include "../monitoring/AbsLimitMonitor.h" #include "../returnvalues/HasReturnvaluesIF.h" diff --git a/power/PowerSensor.h b/power/PowerSensor.h index bb409f3e..a0ccb1ca 100644 --- a/power/PowerSensor.h +++ b/power/PowerSensor.h @@ -1,9 +1,6 @@ #ifndef FSFW_POWER_POWERSENSOR_H_ #define FSFW_POWER_POWERSENSOR_H_ -#include "../datapoolglob/GlobalDataSet.h" -#include "../datapoolglob/GlobalPoolVariable.h" -#include "../datapoolglob/PIDReader.h" #include "../datapoollocal/StaticLocalDataSet.h" #include "../devicehandlers/HealthDevice.h" #include "../monitoring/LimitMonitor.h" diff --git a/thermal/ThermalComponent.cpp b/thermal/ThermalComponent.cpp index 54bd67bd..4805fc9d 100644 --- a/thermal/ThermalComponent.cpp +++ b/thermal/ThermalComponent.cpp @@ -22,22 +22,23 @@ ThermalComponent::~ThermalComponent() { } ReturnValue_t ThermalComponent::setTargetState(int8_t newState) { - GlobDataSet mySet; - gp_int8_t writableTargetState(targetState.getDataPoolId(), - &mySet, PoolVariableIF::VAR_READ_WRITE); - mySet.read(); - if ((writableTargetState == STATE_REQUEST_OPERATIONAL) - && (newState != STATE_REQUEST_IGNORE)) { - return HasReturnvaluesIF::RETURN_FAILED; - } - switch (newState) { - case STATE_REQUEST_NON_OPERATIONAL: - writableTargetState = newState; - mySet.commit(PoolVariableIF::VALID); - return HasReturnvaluesIF::RETURN_OK; - default: - return ThermalComponentCore::setTargetState(newState); - } +// GlobDataSet mySet; +// gp_int8_t writableTargetState(targetState.getDataPoolId(), +// &mySet, PoolVariableIF::VAR_READ_WRITE); +// mySet.read(); +// if ((writableTargetState == STATE_REQUEST_OPERATIONAL) +// && (newState != STATE_REQUEST_IGNORE)) { +// return HasReturnvaluesIF::RETURN_FAILED; +// } +// switch (newState) { +// case STATE_REQUEST_NON_OPERATIONAL: +// writableTargetState = newState; +// mySet.commit(PoolVariableIF::VALID); +// return HasReturnvaluesIF::RETURN_OK; +// default: +// return ThermalComponentCore::setTargetState(newState); +// } + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t ThermalComponent::setLimits(const uint8_t* data, size_t size) { diff --git a/thermal/ThermalComponentCore.cpp b/thermal/ThermalComponentCore.cpp index a64491c0..81d9326f 100644 --- a/thermal/ThermalComponentCore.cpp +++ b/thermal/ThermalComponentCore.cpp @@ -96,25 +96,26 @@ float ThermalComponentCore::getLowerOpLimit() { ReturnValue_t ThermalComponentCore::setTargetState(int8_t newState) { - GlobDataSet mySet; - gp_uint8_t writableTargetState(targetState.getDataPoolId(), - &mySet, PoolVariableIF::VAR_READ_WRITE); - mySet.read(); - if ((writableTargetState == STATE_REQUEST_OPERATIONAL) - && (newState != STATE_REQUEST_IGNORE)) { - return HasReturnvaluesIF::RETURN_FAILED; - } - switch (newState) { - case STATE_REQUEST_HEATING: - case STATE_REQUEST_IGNORE: - case STATE_REQUEST_OPERATIONAL: - writableTargetState = newState; - break; - case STATE_REQUEST_NON_OPERATIONAL: - default: - return INVALID_TARGET_STATE; - } - mySet.commit(PoolVariableIF::VALID); +// GlobDataSet mySet; +// gp_uint8_t writableTargetState(targetState.getDataPoolId(), +// &mySet, PoolVariableIF::VAR_READ_WRITE); +// mySet.read(); +// if ((writableTargetState == STATE_REQUEST_OPERATIONAL) +// && (newState != STATE_REQUEST_IGNORE)) { +// return HasReturnvaluesIF::RETURN_FAILED; +// } +// switch (newState) { +// case STATE_REQUEST_HEATING: +// case STATE_REQUEST_IGNORE: +// case STATE_REQUEST_OPERATIONAL: +// writableTargetState = newState; +// break; +// case STATE_REQUEST_NON_OPERATIONAL: +// default: +// return INVALID_TARGET_STATE; +// } +// mySet.commit(PoolVariableIF::VALID); +// return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK; } diff --git a/thermal/ThermalComponentCore.h b/thermal/ThermalComponentCore.h index 19d72359..da9424e6 100644 --- a/thermal/ThermalComponentCore.h +++ b/thermal/ThermalComponentCore.h @@ -6,8 +6,6 @@ #include "AbstractTemperatureSensor.h" #include "ThermalModule.h" -//#include "../datapoolglob/GlobalDataSet.h" -//#include "../datapoolglob/GlobalPoolVariable.h" #include "../datapoollocal/LocalPoolVariable.h" /** diff --git a/thermal/ThermalModule.cpp b/thermal/ThermalModule.cpp index fbd6939e..2bc1741f 100644 --- a/thermal/ThermalModule.cpp +++ b/thermal/ThermalModule.cpp @@ -1,28 +1,31 @@ -#include "../monitoring/LimitViolationReporter.h" -#include "../monitoring/MonitoringMessageContent.h" #include "ThermalModule.h" - #include "AbstractTemperatureSensor.h" -ThermalModule::ThermalModule(uint32_t moduleTemperaturePoolId, - uint32_t currentStatePoolId, uint32_t targetStatePoolId, - GlobDataSet *dataSet, Parameters parameters, +#include "../monitoring/LimitViolationReporter.h" +#include "../monitoring/MonitoringMessageContent.h" + + +ThermalModule::ThermalModule(gp_id_t moduleTemperaturePoolId, + gp_id_t currentStatePoolId, gp_id_t targetStatePoolId, + LocalPoolDataSetBase *dataSet, Parameters parameters, RedundantHeater::Parameters heaterParameters) : - oldStrategy(ACTIVE_SINGLE), survivalTargetTemp(0), targetTemp(0), heating( - false), parameters(parameters), moduleTemperature( - moduleTemperaturePoolId, dataSet, PoolVariableIF::VAR_WRITE), currentState( - currentStatePoolId, dataSet, PoolVariableIF::VAR_WRITE), targetState( - targetStatePoolId, dataSet, PoolVariableIF::VAR_READ) { + oldStrategy(ACTIVE_SINGLE), parameters(parameters), + moduleTemperature(moduleTemperaturePoolId, dataSet, + PoolVariableIF::VAR_WRITE), + currentState(currentStatePoolId, dataSet, PoolVariableIF::VAR_WRITE), + targetState(targetStatePoolId, dataSet, PoolVariableIF::VAR_READ) { heater = new RedundantHeater(heaterParameters); } -ThermalModule::ThermalModule(uint32_t moduleTemperaturePoolId, GlobDataSet* dataSet) : - oldStrategy(ACTIVE_SINGLE), survivalTargetTemp(0), targetTemp(0), heating( - false), parameters( { 0, 0 }), moduleTemperature( - moduleTemperaturePoolId, dataSet, PoolVariableIF::VAR_WRITE), heater( - NULL), currentState(PoolVariableIF::INVALID, dataSet, - PoolVariableIF::VAR_WRITE), targetState(PoolVariableIF::INVALID, - dataSet, PoolVariableIF::VAR_READ) { +ThermalModule::ThermalModule(gp_id_t moduleTemperaturePoolId, + LocalPoolDataSetBase* dataSet) : + oldStrategy(ACTIVE_SINGLE), parameters( { 0, 0 }), + moduleTemperature(moduleTemperaturePoolId, dataSet, + PoolVariableIF::VAR_WRITE), + currentState(gp_id_t(), dataSet, + PoolVariableIF::VAR_WRITE), + targetState(gp_id_t(), dataSet, + PoolVariableIF::VAR_READ) { } ThermalModule::~ThermalModule() { @@ -30,7 +33,7 @@ ThermalModule::~ThermalModule() { } void ThermalModule::performOperation(uint8_t opCode) { - if (heater != NULL) { + if (heater != nullptr) { heater->performOperation(0); } } @@ -42,7 +45,7 @@ void ThermalModule::performMode(Strategy strategy) { ThermalComponentIF::HeaterRequest componentHeaterRequest = letComponentsPerformAndDeciceIfWeNeedToHeat(safeOnly); - if (heater == NULL) { + if (heater == nullptr) { informComponentsAboutHeaterState(false, NONE); return; } @@ -53,8 +56,8 @@ void ThermalModule::performMode(Strategy strategy) { //Components overwrite the module request. heating = ((componentHeaterRequest == ThermalComponentIF::HEATER_REQUEST_ON) - || (componentHeaterRequest - == ThermalComponentIF::HEATER_REQUEST_EMERGENCY_ON)); + or (componentHeaterRequest + == ThermalComponentIF::HEATER_REQUEST_EMERGENCY_ON)); } bool dual = (strategy == ACTIVE_DUAL); @@ -76,7 +79,7 @@ void ThermalModule::performMode(Strategy strategy) { } float ThermalModule::getTemperature() { - return moduleTemperature; + return moduleTemperature.value; } void ThermalModule::registerSensor(AbstractTemperatureSensor * sensor) { @@ -85,7 +88,8 @@ void ThermalModule::registerSensor(AbstractTemperatureSensor * sensor) { void ThermalModule::registerComponent(ThermalComponentIF* component, ThermalComponentIF::Priority priority) { - components.push_back(ComponentData( { component, priority, ThermalComponentIF::HEATER_DONT_CARE })); + components.push_back(ComponentData( { component, priority, + ThermalComponentIF::HEATER_DONT_CARE })); } void ThermalModule::calculateTemperature() { @@ -94,12 +98,13 @@ void ThermalModule::calculateTemperature() { std::list::iterator iter = sensors.begin(); for (; iter != sensors.end(); iter++) { if ((*iter)->isValid()) { - moduleTemperature = moduleTemperature + (*iter)->getTemperature(); + moduleTemperature = moduleTemperature.value + + (*iter)->getTemperature(); numberOfValidSensors++; } } if (numberOfValidSensors != 0) { - moduleTemperature = moduleTemperature / numberOfValidSensors; + moduleTemperature = moduleTemperature.value / numberOfValidSensors; moduleTemperature.setValid(PoolVariableIF::VALID); } else { moduleTemperature = INVALID_TEMPERATURE; @@ -117,9 +122,10 @@ ThermalComponentIF* ThermalModule::findComponent(object_id_t objectId) { return NULL; } -ThermalComponentIF::HeaterRequest ThermalModule::letComponentsPerformAndDeciceIfWeNeedToHeat( - bool safeOnly) { - ThermalComponentIF::HeaterRequest heaterRequests[ThermalComponentIF::NUMBER_OF_PRIORITIES]; +ThermalComponentIF::HeaterRequest +ThermalModule::letComponentsPerformAndDeciceIfWeNeedToHeat(bool safeOnly) { + ThermalComponentIF::HeaterRequest + heaterRequests[ThermalComponentIF::NUMBER_OF_PRIORITIES]; survivalTargetTemp = -999; targetTemp = -999; @@ -224,7 +230,7 @@ bool ThermalModule::calculateModuleHeaterRequestAndSetModuleStatus( limit = survivalTargetTemp; } - if (moduleTemperature >= limit) { + if (moduleTemperature.value >= limit) { currentState = OPERATIONAL; } else { currentState = NON_OPERATIONAL; @@ -250,15 +256,15 @@ bool ThermalModule::calculateModuleHeaterRequestAndSetModuleStatus( } void ThermalModule::setHeating(bool on) { - GlobDataSet mySet; - gp_int8_t writableTargetState(targetState.getDataPoolId(), - &mySet, PoolVariableIF::VAR_WRITE); - if (on) { - writableTargetState = STATE_REQUEST_HEATING; - } else { - writableTargetState = STATE_REQUEST_PASSIVE; - } - mySet.commit(PoolVariableIF::VALID); +// GlobDataSet mySet; +// gp_int8_t writableTargetState(targetState.getDataPoolId(), +// &mySet, PoolVariableIF::VAR_WRITE); +// if (on) { +// writableTargetState = STATE_REQUEST_HEATING; +// } else { +// writableTargetState = STATE_REQUEST_PASSIVE; +// } +// mySet.commit(PoolVariableIF::VALID); } void ThermalModule::updateTargetTemperatures(ThermalComponentIF* component, diff --git a/thermal/ThermalModule.h b/thermal/ThermalModule.h index 41be6baa..d1e4fccb 100644 --- a/thermal/ThermalModule.h +++ b/thermal/ThermalModule.h @@ -1,14 +1,20 @@ -#ifndef THERMALMODULE_H_ -#define THERMALMODULE_H_ +#ifndef FSFW_THERMAL_THERMALMODULE_H_ +#define FSFW_THERMAL_THERMALMODULE_H_ -#include "../datapoolglob/GlobalDataSet.h" -#include "../datapoolglob/GlobalPoolVariable.h" -#include "../devicehandlers/HealthDevice.h" -#include "../events/EventReportingProxyIF.h" #include "ThermalModuleIF.h" -#include #include "tcsDefinitions.h" #include "RedundantHeater.h" + +//#include "../datapoolglob/GlobalDataSet.h" +//#include "../datapoolglob/GlobalPoolVariable.h" +#include "../datapoollocal/LocalPoolDataSetBase.h" +#include "../datapoollocal/LocalPoolVariable.h" +#include "../devicehandlers/HealthDevice.h" +#include "../events/EventReportingProxyIF.h" + +#include + + class PowerSwitchIF; /** @@ -22,11 +28,12 @@ public: float hysteresis; }; - ThermalModule(uint32_t moduleTemperaturePoolId, uint32_t currentStatePoolId, - uint32_t targetStatePoolId, GlobDataSet *dataSet, Parameters parameters, - RedundantHeater::Parameters heaterParameters); + ThermalModule(gp_id_t moduleTemperaturePoolId, gp_id_t currentStatePoolId, + gp_id_t targetStatePoolId, LocalPoolDataSetBase *dataSet, + Parameters parameters, RedundantHeater::Parameters heaterParameters); - ThermalModule(uint32_t moduleTemperaturePoolId, GlobDataSet *dataSet); + ThermalModule(gp_id_t moduleTemperaturePoolId, + LocalPoolDataSetBase *dataSet); virtual ~ThermalModule(); @@ -62,20 +69,21 @@ protected: Strategy oldStrategy; - float survivalTargetTemp; + float survivalTargetTemp = 0.0; - float targetTemp; + float targetTemp = 0.0; - bool heating; + bool heating = false; Parameters parameters; - gp_float_t moduleTemperature; + lp_var_t moduleTemperature; + //gp_float_t moduleTemperature; - RedundantHeater *heater; + RedundantHeater *heater = nullptr; - gp_int8_t currentState; - gp_int8_t targetState; + lp_var_t currentState; + lp_var_t targetState; std::list sensors; std::list components; @@ -92,4 +100,4 @@ protected: void updateTargetTemperatures(ThermalComponentIF *component, bool isSafe); }; -#endif /* THERMALMODULE_H_ */ +#endif /* FSFW_THERMAL_THERMALMODULE_H_ */ From f108ae883c1a19eaa74f1c67cd4278815c98538a Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 16:28:16 +0100 Subject: [PATCH 09/22] local pool replacement --- thermal/ThermalComponentCore.cpp | 39 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/thermal/ThermalComponentCore.cpp b/thermal/ThermalComponentCore.cpp index 81d9326f..29f9e25d 100644 --- a/thermal/ThermalComponentCore.cpp +++ b/thermal/ThermalComponentCore.cpp @@ -96,26 +96,25 @@ float ThermalComponentCore::getLowerOpLimit() { ReturnValue_t ThermalComponentCore::setTargetState(int8_t newState) { -// GlobDataSet mySet; -// gp_uint8_t writableTargetState(targetState.getDataPoolId(), -// &mySet, PoolVariableIF::VAR_READ_WRITE); -// mySet.read(); -// if ((writableTargetState == STATE_REQUEST_OPERATIONAL) -// && (newState != STATE_REQUEST_IGNORE)) { -// return HasReturnvaluesIF::RETURN_FAILED; -// } -// switch (newState) { -// case STATE_REQUEST_HEATING: -// case STATE_REQUEST_IGNORE: -// case STATE_REQUEST_OPERATIONAL: -// writableTargetState = newState; -// break; -// case STATE_REQUEST_NON_OPERATIONAL: -// default: -// return INVALID_TARGET_STATE; -// } -// mySet.commit(PoolVariableIF::VALID); -// return HasReturnvaluesIF::RETURN_OK; + targetState.setReadWriteMode(pool_rwm_t::VAR_READ_WRITE); + targetState.read(); + if((targetState == STATE_REQUEST_OPERATIONAL) and + (newState != STATE_REQUEST_IGNORE)) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + switch (newState) { + case STATE_REQUEST_HEATING: + case STATE_REQUEST_IGNORE: + case STATE_REQUEST_OPERATIONAL: + targetState = newState; + break; + case STATE_REQUEST_NON_OPERATIONAL: + default: + return INVALID_TARGET_STATE; + } + targetState.setValid(true); + targetState.commit(); return HasReturnvaluesIF::RETURN_OK; } From d1beb96c6030c82bf20d65eea0196cb4d904c8a2 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 16:30:20 +0100 Subject: [PATCH 10/22] some more replacements --- thermal/ThermalComponent.cpp | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/thermal/ThermalComponent.cpp b/thermal/ThermalComponent.cpp index 4805fc9d..084201dd 100644 --- a/thermal/ThermalComponent.cpp +++ b/thermal/ThermalComponent.cpp @@ -22,22 +22,21 @@ ThermalComponent::~ThermalComponent() { } ReturnValue_t ThermalComponent::setTargetState(int8_t newState) { -// GlobDataSet mySet; -// gp_int8_t writableTargetState(targetState.getDataPoolId(), -// &mySet, PoolVariableIF::VAR_READ_WRITE); -// mySet.read(); -// if ((writableTargetState == STATE_REQUEST_OPERATIONAL) -// && (newState != STATE_REQUEST_IGNORE)) { -// return HasReturnvaluesIF::RETURN_FAILED; -// } -// switch (newState) { -// case STATE_REQUEST_NON_OPERATIONAL: -// writableTargetState = newState; -// mySet.commit(PoolVariableIF::VALID); -// return HasReturnvaluesIF::RETURN_OK; -// default: -// return ThermalComponentCore::setTargetState(newState); -// } + targetState.setReadWriteMode(pool_rwm_t::VAR_READ_WRITE); + targetState.read(); + if ((targetState == STATE_REQUEST_OPERATIONAL) + and (newState != STATE_REQUEST_IGNORE)) { + return HasReturnvaluesIF::RETURN_FAILED; + } + switch (newState) { + case STATE_REQUEST_NON_OPERATIONAL: + targetState = newState; + targetState.setValid(true); + targetState.commit(PoolVariableIF::VALID); + return HasReturnvaluesIF::RETURN_OK; + default: + return ThermalComponentCore::setTargetState(newState); + } return HasReturnvaluesIF::RETURN_OK; } @@ -79,13 +78,14 @@ ThermalComponentIF::State ThermalComponent::getState(float temperature, } void ThermalComponent::checkLimits(ThermalComponentIF::State state) { - //if (targetState == STATE_REQUEST_OPERATIONAL || targetState == STATE_REQUEST_IGNORE) { - // ThermalComponentCore::checkLimits(state); - // return; - //} - //If component is not operational, it checks the NOP limits. - //temperatureMonitor.translateState(state, temperature.value, - // nopParameters.lowerNopLimit, nopParameters.upperNopLimit, false); + if ((targetState == STATE_REQUEST_OPERATIONAL) or + (targetState == STATE_REQUEST_IGNORE)) { + ThermalComponentCore::checkLimits(state); + return; + } + // If component is not operational, it checks the NOP limits. + temperatureMonitor.translateState(state, temperature.value, + nopParameters.lowerNopLimit, nopParameters.upperNopLimit, false); } ThermalComponentIF::HeaterRequest ThermalComponent::getHeaterRequest( From ef2e07b3891a3c2a2429d973d5db68208db7e526 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Mon, 30 Nov 2020 17:03:14 +0100 Subject: [PATCH 11/22] added explicit type conversion operator --- datapoollocal/LocalPoolVariable.h | 4 ++++ datapoollocal/LocalPoolVariable.tpp | 32 ++++++++++++++++------------- thermal/ThermalComponentCore.cpp | 6 +++--- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/datapoollocal/LocalPoolVariable.h b/datapoollocal/LocalPoolVariable.h index bb36089d..c18d5443 100644 --- a/datapoollocal/LocalPoolVariable.h +++ b/datapoollocal/LocalPoolVariable.h @@ -121,6 +121,10 @@ public: LocalPoolVar &operator=(const T& newValue); LocalPoolVar &operator=(const LocalPoolVar& 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 LocalPoolVar& other) const; bool operator==(const T& other) const; diff --git a/datapoollocal/LocalPoolVariable.tpp b/datapoollocal/LocalPoolVariable.tpp index a63e550d..b9f7b906 100644 --- a/datapoollocal/LocalPoolVariable.tpp +++ b/datapoollocal/LocalPoolVariable.tpp @@ -80,20 +80,6 @@ inline ReturnValue_t LocalPoolVar::commitWithoutLock() { return RETURN_OK; } -template -inline LocalPoolVar & LocalPoolVar::operator=(const T& newValue) { - value = newValue; - return *this; -} - -template -inline LocalPoolVar& LocalPoolVar::operator =( - const LocalPoolVar& newPoolVariable) { - value = newPoolVariable.value; - return *this; -} - - template inline ReturnValue_t LocalPoolVar::serialize(uint8_t** buffer, size_t* size, const size_t max_size, SerializeIF::Endianness streamEndianness) const { @@ -119,6 +105,24 @@ inline std::ostream& operator<< (std::ostream &out, return out; } +template +inline LocalPoolVar::operator T() const { + return value; +} + +template +inline LocalPoolVar & LocalPoolVar::operator=(const T& newValue) { + value = newValue; + return *this; +} + +template +inline LocalPoolVar& LocalPoolVar::operator =( + const LocalPoolVar& newPoolVariable) { + value = newPoolVariable.value; + return *this; +} + template inline bool LocalPoolVar::operator ==(const LocalPoolVar &other) const { return this->value == other.value; diff --git a/thermal/ThermalComponentCore.cpp b/thermal/ThermalComponentCore.cpp index 29f9e25d..7b594d0c 100644 --- a/thermal/ThermalComponentCore.cpp +++ b/thermal/ThermalComponentCore.cpp @@ -60,7 +60,7 @@ ThermalComponentIF::HeaterRequest ThermalComponentCore::performOperation( //SHOULDDO: Better pass db_float_t* to getTemperature and set it invalid if invalid. temperature = getTemperature(); updateMinMaxTemp(); - if ((temperature != INVALID_TEMPERATURE)) { + if (temperature != INVALID_TEMPERATURE) { temperature.setValid(PoolVariableIF::VALID); State state = getState(temperature.value, getParameters(), targetState.value); @@ -230,10 +230,10 @@ void ThermalComponentCore::updateMinMaxTemp() { return; } if (temperature < minTemp) { - minTemp = temperature.value; + minTemp = static_cast(temperature); } if (temperature > maxTemp) { - maxTemp = temperature.value; + maxTemp = static_cast(temperature); } } From f357de21c9b37a5ca384590be0d511c0c503aa16 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 13:23:51 +0100 Subject: [PATCH 12/22] updated testcfg --- unittest/testcfg/objects/translateObjects.cpp | 242 ------------------ unittest/testcfg/objects/translateObjects.h | 16 -- 2 files changed, 258 deletions(-) delete mode 100644 unittest/testcfg/objects/translateObjects.cpp delete mode 100644 unittest/testcfg/objects/translateObjects.h diff --git a/unittest/testcfg/objects/translateObjects.cpp b/unittest/testcfg/objects/translateObjects.cpp deleted file mode 100644 index abcb1b40..00000000 --- a/unittest/testcfg/objects/translateObjects.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* Auto-generated event translation file. Contains 77 translations. */ -#include - -const char *AT91_UART2_TEST_TASK_STRING = "AT91_UART2_TEST_TASK"; -const char *ARDUINO_0_STRING = "ARDUINO_0"; -const char *ARDUINO_1_STRING = "ARDUINO_1"; -const char *ARDUINO_2_STRING = "ARDUINO_2"; -const char *ARDUINO_3_STRING = "ARDUINO_3"; -const char *ARDUINO_4_STRING = "ARDUINO_4"; -const char *AT91_I2C_TEST_TASK_STRING = "AT91_I2C_TEST_TASK"; -const char *TEST_TASK_STRING = "TEST_TASK"; -const char *PCDU_HANDLER_STRING = "PCDU_HANDLER"; -const char *DUMMY_HANDLER_STRING = "DUMMY_HANDLER"; -const char *S_ADC1_DEC2_STRING = "S_ADC1_DEC2"; -const char *GPS0_HANDLER_STRING = "GPS0_HANDLER"; -const char *DLR_PVCH_STRING = "DLR_PVCH"; -const char *GYRO1_STRING = "GYRO1"; -const char *DLR_IRAS_STRING = "DLR_IRAS"; -const char *_DEC1_O1_STRING = "_DEC1_O1"; -const char *_DEC1_O2_STRING = "_DEC1_O2"; -const char *S1_DEC1_O3_STRING = "S1_DEC1_O3"; -const char *S2_DEC1_O4_STRING = "S2_DEC1_O4"; -const char *S3_DEC1_O5_STRING = "S3_DEC1_O5"; -const char *PT1000_PVHC_DEC1_O6_STRING = "PT1000_PVHC_DEC1_O6"; -const char *PT1000_CCSDS1_DEC2_STRING = "PT1000_CCSDS1_DEC2"; -const char *PT1000_MGT1_DEC2_STRING = "PT1000_MGT1_DEC2"; -const char *S4_DEC2_STRING = "S4_DEC2"; -const char *S5_DEC2_STRING = "S5_DEC2"; -const char *S6_DEC2_STRING = "S6_DEC2"; -const char *PT1000_PVCH_DEC2_STRING = "PT1000_PVCH_DEC2"; -const char *_DEC3_STRING = "_DEC3"; -const char *PT1000_CCSDS2_DEC3_STRING = "PT1000_CCSDS2_DEC3"; -const char *S7_DEC3_STRING = "S7_DEC3"; -const char *S8_DEC3_STRING = "S8_DEC3"; -const char *PT1000_PVCH_DEC3_STRING = "PT1000_PVCH_DEC3"; -const char *GYRO2_STRING = "GYRO2"; -const char *PT1000_PLOC_DEC4_STRING = "PT1000_PLOC_DEC4"; -const char *S9_DEC4_STRING = "S9_DEC4"; -const char *S10_DEC4_STRING = "S10_DEC4"; -const char *PT1000_PVHC_DEC4_STRING = "PT1000_PVHC_DEC4"; -const char *S_ADC_DEC4_STRING = "S_ADC_DEC4"; -const char *GPS1_HANDLER_STRING = "GPS1_HANDLER"; -const char *DUMMY_GPS_COM_IF_STRING = "DUMMY_GPS_COM_IF"; -const char *RS232_DEVICE_COM_IF_STRING = "RS232_DEVICE_COM_IF"; -const char *I2C_DEVICE_COM_IF_STRING = "I2C_DEVICE_COM_IF"; -const char *GPIO_DEVICE_COM_IF_STRING = "GPIO_DEVICE_COM_IF"; -const char *SPI_POLLING_TASK_STRING = "SPI_POLLING_TASK"; -const char *SPI_DEVICE_COM_IF_STRING = "SPI_DEVICE_COM_IF"; -const char *DUMMY_ECHO_COM_IF_STRING = "DUMMY_ECHO_COM_IF"; -const char *SD_CARD_HANDLER_STRING = "SD_CARD_HANDLER"; -const char *CCSDS_PACKET_DISTRIBUTOR_STRING = "CCSDS_PACKET_DISTRIBUTOR"; -const char *PUS_PACKET_DISTRIBUTOR_STRING = "PUS_PACKET_DISTRIBUTOR"; -const char *UDP_TMTC_BRIDGE_STRING = "UDP_TMTC_BRIDGE"; -const char *EMAC_POLLING_TASK_STRING = "EMAC_POLLING_TASK"; -const char *SERIAL_POLLING_TASK_STRING = "SERIAL_POLLING_TASK"; -const char *UART_TMTC_BRIDGE_STRING = "UART_TMTC_BRIDGE"; -const char *PUS_SERVICE_1_STRING = "PUS_SERVICE_1"; -const char *PUS_SERVICE_2_STRING = "PUS_SERVICE_2"; -const char *PUS_SERVICE_3_STRING = "PUS_SERVICE_3"; -const char *PUS_SERVICE_5_STRING = "PUS_SERVICE_5"; -const char *PUS_SERVICE_6_STRING = "PUS_SERVICE_6"; -const char *PUS_SERVICE_8_STRING = "PUS_SERVICE_8"; -const char *PUS_SERVICE_9_STRING = "PUS_SERVICE_9"; -const char *PUS_SERVICE_17_STRING = "PUS_SERVICE_17"; -const char *PUS_SERVICE_23_STRING = "PUS_SERVICE_23"; -const char *PUS_SERVICE_200_STRING = "PUS_SERVICE_200"; -const char *PUS_SERVICE_201_STRING = "PUS_SERVICE_201"; -const char *PUS_TIME_STRING = "PUS_TIME"; -const char *PUS_FUNNEL_STRING = "PUS_FUNNEL"; -const char *HEALTH_TABLE_STRING = "HEALTH_TABLE"; -const char *MODE_STORE_STRING = "MODE_STORE"; -const char *EVENT_MANAGER_STRING = "EVENT_MANAGER"; -const char *INTERNAL_ERROR_REPORTER_STRING = "INTERNAL_ERROR_REPORTER"; -const char *TC_STORE_STRING = "TC_STORE"; -const char *TM_STORE_STRING = "TM_STORE"; -const char *IPC_STORE_STRING = "IPC_STORE"; -const char *AT91_SPI_TEST_TASK_STRING = "AT91_SPI_TEST_TASK"; -const char *STM32_TEST_TASK_STRING = "STM32_TEST_TASK"; -const char *AT91_UART0_TEST_TASK_STRING = "AT91_UART0_TEST_TASK"; -const char *NO_OBJECT_STRING = "NO_OBJECT"; - -const char* translateObject(object_id_t object){ - switch((object&0xFFFFFFFF)){ - case 0x000123336: - return AT91_UART2_TEST_TASK_STRING; - case 0x01010100: - return ARDUINO_0_STRING; - case 0x01010101: - return ARDUINO_1_STRING; - case 0x01010102: - return ARDUINO_2_STRING; - case 0x01010103: - return ARDUINO_3_STRING; - case 0x01010104: - return ARDUINO_4_STRING; - case 0x12345678: - return AT91_I2C_TEST_TASK_STRING; - case 0x42694269: - return TEST_TASK_STRING; - case 0x44003200: - return PCDU_HANDLER_STRING; - case 0x4400AFFE: - return DUMMY_HANDLER_STRING; - case 0x44020108: - return S_ADC1_DEC2_STRING; - case 0x44101F00: - return GPS0_HANDLER_STRING; - case 0x44104000: - return DLR_PVCH_STRING; - case 0x44105000: - return GYRO1_STRING; - case 0x44106000: - return DLR_IRAS_STRING; - case 0x44115401: - return _DEC1_O1_STRING; - case 0x44115402: - return _DEC1_O2_STRING; - case 0x44115404: - return S1_DEC1_O3_STRING; - case 0x44115405: - return S2_DEC1_O4_STRING; - case 0x44115406: - return S3_DEC1_O5_STRING; - case 0x44115407: - return PT1000_PVHC_DEC1_O6_STRING; - case 0x44125401: - return PT1000_CCSDS1_DEC2_STRING; - case 0x44125403: - return PT1000_MGT1_DEC2_STRING; - case 0x44125404: - return S4_DEC2_STRING; - case 0x44125405: - return S5_DEC2_STRING; - case 0x44125406: - return S6_DEC2_STRING; - case 0x44125407: - return PT1000_PVCH_DEC2_STRING; - case 0x44130301: - return _DEC3_STRING; - case 0x44130302: - return PT1000_CCSDS2_DEC3_STRING; - case 0x44130305: - return S7_DEC3_STRING; - case 0x44130306: - return S8_DEC3_STRING; - case 0x44130307: - return PT1000_PVCH_DEC3_STRING; - case 0x44130308: - return GYRO2_STRING; - case 0x44145401: - return PT1000_PLOC_DEC4_STRING; - case 0x44145404: - return S9_DEC4_STRING; - case 0x44145405: - return S10_DEC4_STRING; - case 0x44145406: - return PT1000_PVHC_DEC4_STRING; - case 0x44145407: - return S_ADC_DEC4_STRING; - case 0x44202000: - return GPS1_HANDLER_STRING; - case 0x49001F00: - return DUMMY_GPS_COM_IF_STRING; - case 0x49005200: - return RS232_DEVICE_COM_IF_STRING; - case 0x49005300: - return I2C_DEVICE_COM_IF_STRING; - case 0x49005400: - return GPIO_DEVICE_COM_IF_STRING; - case 0x49005410: - return SPI_POLLING_TASK_STRING; - case 0x49005600: - return SPI_DEVICE_COM_IF_STRING; - case 0x4900AFFE: - return DUMMY_ECHO_COM_IF_STRING; - case 0x4D0073AD: - return SD_CARD_HANDLER_STRING; - case 0x50000100: - return CCSDS_PACKET_DISTRIBUTOR_STRING; - case 0x50000200: - return PUS_PACKET_DISTRIBUTOR_STRING; - case 0x50000300: - return UDP_TMTC_BRIDGE_STRING; - case 0x50000400: - return EMAC_POLLING_TASK_STRING; - case 0x50000600: - return SERIAL_POLLING_TASK_STRING; - case 0x5000500: - return UART_TMTC_BRIDGE_STRING; - case 0x51000100: - return PUS_SERVICE_1_STRING; - case 0x51000200: - return PUS_SERVICE_2_STRING; - case 0x51000300: - return PUS_SERVICE_3_STRING; - case 0x51000400: - return PUS_SERVICE_5_STRING; - case 0x51000500: - return PUS_SERVICE_6_STRING; - case 0x51000800: - return PUS_SERVICE_8_STRING; - case 0x51000900: - return PUS_SERVICE_9_STRING; - case 0x51001700: - return PUS_SERVICE_17_STRING; - case 0x51002300: - return PUS_SERVICE_23_STRING; - case 0x51020000: - return PUS_SERVICE_200_STRING; - case 0x51020100: - return PUS_SERVICE_201_STRING; - case 0x52000001: - return PUS_TIME_STRING; - case 0x52000002: - return PUS_FUNNEL_STRING; - case 0x53010000: - return HEALTH_TABLE_STRING; - case 0x53010100: - return MODE_STORE_STRING; - case 0x53030000: - return EVENT_MANAGER_STRING; - case 0x53040000: - return INTERNAL_ERROR_REPORTER_STRING; - case 0x534f0100: - return TC_STORE_STRING; - case 0x534f0200: - return TM_STORE_STRING; - case 0x534f0300: - return IPC_STORE_STRING; - case 0x66666666: - return AT91_SPI_TEST_TASK_STRING; - case 0x77777777: - return STM32_TEST_TASK_STRING; - case 0x87654321: - return AT91_UART0_TEST_TASK_STRING; - case 0xFFFFFFFF: - return NO_OBJECT_STRING; - default: - return "UNKNOWN_OBJECT"; - } - return 0; -} diff --git a/unittest/testcfg/objects/translateObjects.h b/unittest/testcfg/objects/translateObjects.h deleted file mode 100644 index 18c237eb..00000000 --- a/unittest/testcfg/objects/translateObjects.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * translateObjects.h - * - * Created on: 28 May 2019 - * Author: Robin - */ - -#ifndef CONFIG_OBJECTS_TRANSLATEOBJECTS_H_ -#define CONFIG_OBJECTS_TRANSLATEOBJECTS_H_ - -#include - -const char* translateObject(object_id_t object); - - -#endif /* CONFIG_OBJECTS_TRANSLATEOBJECTS_H_ */ From e323c69f20da5b2321fbecd5ef2ab84dc1b2060d Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 13:36:30 +0100 Subject: [PATCH 13/22] better name for preproc define --- defaultcfg/fsfwconfig/FSFWConfig.h | 8 ++++---- storagemanager/LocalPool.cpp | 9 +++++---- storagemanager/PoolManager.cpp | 3 ++- unittest/testcfg/FSFWConfig.h | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/defaultcfg/fsfwconfig/FSFWConfig.h b/defaultcfg/fsfwconfig/FSFWConfig.h index f475e3fa..1386bf66 100644 --- a/defaultcfg/fsfwconfig/FSFWConfig.h +++ b/defaultcfg/fsfwconfig/FSFWConfig.h @@ -8,12 +8,12 @@ //! Those can lead to code bloat. #define FSFW_CPP_OSTREAM_ENABLED 1 -//! Reduced printout to further decrese code size +//! Reduced printout to further decrease code size //! Be careful, this also turns off most diagnostic prinouts! -#define FSFW_REDUCED_PRINTOUT 0 +#define FSFW_ENHANCED_PRINTOUT 0 -//! Can be used to enable debugging printouts for developing the FSFW -#define FSFW_DEBUGGING 0 +//! Can be used to enable additional debugging printouts for developing the FSFW +#define FSFW_PRINT_VERBOSITY_LEVEL 0 //! Defines the FIFO depth of each commanding service base which //! also determines how many commands a CSB service can handle in one cycle diff --git a/storagemanager/LocalPool.cpp b/storagemanager/LocalPool.cpp index 8e5f4d48..74e362d4 100644 --- a/storagemanager/LocalPool.cpp +++ b/storagemanager/LocalPool.cpp @@ -1,4 +1,5 @@ #include "LocalPool.h" +#include #include LocalPool::LocalPool(object_id_t setObjectId, const LocalPoolConfig& poolConfig, @@ -116,7 +117,7 @@ ReturnValue_t LocalPool::modifyData(store_address_t storeId, } ReturnValue_t LocalPool::deleteData(store_address_t storeId) { -#if FSFW_DEBUGGING == 1 +#if FSFW_VERBOSE_PRINTOUT == 2 sif::debug << "Delete: Pool: " << std::dec << storeId.poolIndex << " Index: " << storeId.packetIndex << std::endl; @@ -155,7 +156,7 @@ ReturnValue_t LocalPool::deleteData(uint8_t *ptr, size_t size, // element of an object. localId.packetIndex = deltaAddress / elementSizes[n]; result = deleteData(localId); -#if FSFW_DEBUGGING == 1 +#if FSFW_VERBOSE_PRINTOUT == 2 if (deltaAddress % elementSizes[n] != 0) { sif::error << "LocalPool::deleteData: Address not aligned!" << std::endl; @@ -222,7 +223,7 @@ ReturnValue_t LocalPool::reserveSpace(const size_t size, status = findEmpty(storeId->poolIndex, &storeId->packetIndex); } if (status == RETURN_OK) { -#if FSFW_DEBUGGING == 1 +#if FSFW_VERBOSE_PRINTOUT == 2 sif::debug << "Reserve: Pool: " << std::dec << storeId->poolIndex << " Index: " << storeId->packetIndex << std::endl; @@ -264,7 +265,7 @@ void LocalPool::setToSpillToHigherPools(bool enable) { ReturnValue_t LocalPool::getPoolIndex(size_t packetSize, uint16_t *poolIndex, uint16_t startAtIndex) { for (uint16_t n = startAtIndex; n < NUMBER_OF_POOLS; n++) { -#if FSFW_DEBUGGING == 1 +#if FSFW_VERBOSE_PRINTOUT == 2 sif::debug << "LocalPool " << getObjectId() << "::getPoolIndex: Pool: " << n << ", Element Size: " << elementSizes[n] << std::endl; #endif diff --git a/storagemanager/PoolManager.cpp b/storagemanager/PoolManager.cpp index 9c801a3d..3b7b549b 100644 --- a/storagemanager/PoolManager.cpp +++ b/storagemanager/PoolManager.cpp @@ -1,4 +1,5 @@ #include "PoolManager.h" +#include PoolManager::PoolManager(object_id_t setObjectId, const LocalPoolConfig& localPoolConfig): @@ -24,7 +25,7 @@ ReturnValue_t PoolManager::reserveSpace(const size_t size, ReturnValue_t PoolManager::deleteData( store_address_t storeId) { -#if FSFW_DEBUGGING == 1 +#if FSFW_VERBOSE_PRINTOUT == 2 sif::debug << "PoolManager( " << translateObject(getObjectId()) << " )::deleteData from store " << storeId.poolIndex << ". id is "<< storeId.packetIndex << std::endl; diff --git a/unittest/testcfg/FSFWConfig.h b/unittest/testcfg/FSFWConfig.h index 4fb224c1..d3451985 100644 --- a/unittest/testcfg/FSFWConfig.h +++ b/unittest/testcfg/FSFWConfig.h @@ -12,7 +12,7 @@ #define FSFW_REDUCED_PRINTOUT 0 //! Can be used to enable debugging printouts for developing the FSFW -#define FSFW_DEBUGGING 0 +#define FSFW_VERBOSE_PRINTOUT 0 //! Defines the FIFO depth of each commanding service base which //! also determines how many commands a CSB service can handle in one cycle From e0f063f232f92931f17d874de122d9c30c795d73 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 17:14:44 +0100 Subject: [PATCH 14/22] comment added --- memory/HasFileSystemIF.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/memory/HasFileSystemIF.h b/memory/HasFileSystemIF.h index 20ef727e..dcec6346 100644 --- a/memory/HasFileSystemIF.h +++ b/memory/HasFileSystemIF.h @@ -8,7 +8,9 @@ #include /** - * @author Jakob Meier + * @brief Generic interface for objects which expose a file system to enable + * message based file handling. + * @author J. Meier, R. Mueller */ class HasFileSystemIF { public: From 314dc577b547f734ef32b0552d109a9b5f88e7b4 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 17:19:00 +0100 Subject: [PATCH 15/22] added changelog from upstream --- CHANGELOG | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 CHANGELOG diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 00000000..a6686478 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,13 @@ +## Changes from ASTP 0.0.1 to 1.0.0 + + +### FreeRTOS OSAL + +- vRequestContextSwitchFromISR is declared extern "C" so it can be defined in +a C file without issues + +### PUS Services + +- It is now possible to change the message queue depth for the telecommand verification service (PUS1) +- The same is possible for the event reporting service (PUS5) +- PUS Health Service added, which allows to command and retrieve health via PUS packets From 84ab5f83189481909f23dc7016082f51010fdfa0 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 17:19:26 +0100 Subject: [PATCH 16/22] readme and logo update --- README.md | 161 ++++++++- logo/FSFW_Logo_V3.png | Bin 0 -> 137423 bytes logo/FSFW_Logo_V3.svg | 711 +++++++++++++++++++++++++++++++++++++++ logo/FSFW_Logo_V3_bw.png | Bin 0 -> 13369 bytes 4 files changed, 869 insertions(+), 3 deletions(-) create mode 100644 logo/FSFW_Logo_V3.png create mode 100644 logo/FSFW_Logo_V3.svg create mode 100644 logo/FSFW_Logo_V3_bw.png diff --git a/README.md b/README.md index fc86fca7..4aabe36a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,159 @@ -Flight Software Framework (FSFW) -====== +![FSFW Logo](logo/FSFW_Logo_V3_bw.png) +# Flight Software Framework (FSFW) -I want to be written! +The Flight Software Framework is a C++ Object Oriented Framework for unmanned, +automated systems like Satellites. + +The initial version of the Flight Software Framework was developed during +the Flying Laptop Project by the University of Stuttgart in cooperation +with Airbus Defence and Space GmbH. + +## Intended Use + +The framework is designed for systems, which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. +Therefore, a mode and health system provides control over the states of the software and the controlled devices. +In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well. + +The recommended hardware is a microprocessor with more than 2 MB of RAM and 1 MB of non-volatile Memory. +For reference, current Applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC. + + +## Structure + +The general structure is driven by the usage of interfaces provided by objects. The FSFW uses C++11 as baseline. The intention behind this is that this C++ Standard should be widely available, even with older compilers. +The FSFW uses dynamic allocation during the initialization but provides static containers during runtime. +This simplifies the instantiation of objects and allows the usage of some standard containers. +Dynamic Allocation after initialization is discouraged and different solutions are provided in the FSFW to achieve that. +The fsfw uses Run-time type information. +Exceptions are not allowed. + +### Failure Handling + +Functions should return a defined ReturnValue_t to signal to the caller that something is gone wrong. +Returnvalues must be unique. For this the function HasReturnvaluesIF::makeReturnCode or the Macro MAKE_RETURN can be used. +The CLASS_ID is a unique id for that type of object. See returnvalues/FwClassIds. + +### OSAL +The FSFW provides operation system abstraction layers for Linux, FreeRTOS and RTEMS. A independent OSAL called "host" is currently not finished. This aims to be running on windows as well. +The OSAL provides periodic tasks, message queues, clocks and Semaphores as well as Mutexes. + +### Core Components + +Clock: + * This is a class of static functions that can be used at anytime + * Leap Seconds must be set if any time conversions from UTC to other times is used + +ObjectManager (must be created): + +* The component which handles all references. All SystemObjects register at this component. +* Any SystemObject needs to have a unique ObjectId. Those can be managed like objects::framework_objects. +* A reference to an object can be get by calling the following function. T must be the specific Interface you want to call. +A nullptr check of the returning Pointer must be done. This function is based on Run-time type information. + +``` c++ + template T* ObjectManagerIF::get( object_id_t id ) + +``` +* A typical way to create all objects on startup is a handing a static produce function to the ObjectManager on creation. +By calling objectManager->initialize() the produce function will be called and all SystemObjects will be initialized afterwards. + +Event Manager: + +* Component which allows routing of events +* Other objects can subscribe to specific events, ranges of events or all events of an object. +* Subscriptions can be done during runtime but should be done during initialization +* Amounts of allowed subscriptions must be configured by setting this parameters: + +``` c++ +namespace fsfwconfig { +//! Configure the allocated pool sizes for the event manager. +static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240; +static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120; +static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120; +} +``` + + +Health Table: + +* A component which holds every health state +* Provides a thread safe way to access all health states without the need of message exchanges + +Stores + +* The message based communication can only exchange a few bytes of information inside the message itself. Therefore, additional information can be exchanged with Stores. With this, only the store address must be exchanged in the message. +* Internally, the FSFW uses an IPC Store to exchange data between processes. For incoming TCs a TC Store is used. For outgoing TM a TM store is used. +* All of them should use the Thread Safe Class storagemanager/PoolManager + +Tasks + +There are two different types of tasks: + * The PeriodicTask just executes objects that are of type ExecutableObjectIF in the order of the insertion to the Tasks. + * FixedTimeslotTask executes a list of calls in the order of the given list. This is intended for DeviceHandlers, where polling should be in a defined order. An example can be found in defaultcfg/fsfwconfig/pollingSequence + + +### Static Ids in the framework + +Some parts of the framework use a static routing address for communication. +An example setup of ids can be found in the example config in "defaultcft/fsfwconfig/objects/Factory::setStaticFrameworkObjectIds()". + +### Events + +Events are tied to objects. EventIds can be generated by calling the Macro MAKE_EVENT. This works analog to the returnvalues. +Every object that needs own EventIds has to get a unique SUBSYSTEM_ID. +Every SystemObject can call triggerEvent from the parent class. +Therefore, event messages contain the specific EventId and the objectId of the object that has triggered. + +### Internal Communication + +Components communicate mostly over Message through Queues. +Those queues are created by calling the singleton QueueFactory::instance()->create(). + +### External Communication + +The external communication with the mission control system is mostly up to the user implementation. +The FSFW provides PUS Services which can be used to but don't need to be used. +The services can be seen as a conversion from a TC to a message based communication and back. + +#### CCSDS Frames, CCSDS Space Packets and PUS + +If the communication is based on CCSDS Frames and Space Packets, several classes can be used to distributed the packets to the corresponding services. Those can be found in tcdistribution. +If Space Packets are used, a timestamper must be created. +An example can be found in the timemanager folder, this uses CCSDSTime::CDS_short. + +#### DeviceHandling + +DeviceHandlers are a core component of the FSFW. +The idea is, to have a software counterpart of every physical device to provide a simple mode, health and commanding interface. +By separating the underlying Communication Interface with DeviceCommunicationIF, a DH can be tested on different hardware. +The DH has mechanisms to monitor the communication with the physical device which allow for FDIR reaction. +A standard FDIR component for the DH will be created automatically but can be overwritten by the user. + +#### Modes, Health + +The two interfaces HasModesIF and HasHealthIF provide access for commanding and monitoring of components. +On-board Mode Management is implement in hierarchy system. +DeviceHandlers and Controllers are the lowest part of the hierarchy. +The next layer are Assemblies. Those assemblies act as a component which handle redundancies of handlers. +Assemblies share a common core with the next level which are the Subsystems. + +Those Assemblies are intended to act as auto-generated components from a database which describes the subsystem modes. +The definitions contain transition and target tables which contain the DH, Assembly and Controller Modes to be commanded. +Transition tables contain as many steps as needed to reach the mode from any other mode, e.g. a switch into any higher AOCS mode might first turn on the sensors, than the actuators and the controller as last component. +The target table is used to describe the state that is checked continuously by the subsystem. +All of this allows System Modes to be generated as Subsystem object as well from the same database. +This System contains list of subsystem modes in the transition and target tables. +Therefore, it allows a modular system to create system modes and easy commanding of those, because only the highest components must be commanded. + +The health state represents if the component is able to perform its tasks. +This can be used to signal the system to avoid using this component instead of a redundant one. +The on-board FDIR uses the health state for isolation and recovery. + +## Example config + +A example config can be found in defaultcfg/fsfwconfig. + +## Unit Tests + +Unit Tests are provided in the unittest folder. Those use the catch2 framework but do not include catch2 itself. +See README.md in the unittest Folder. \ No newline at end of file diff --git a/logo/FSFW_Logo_V3.png b/logo/FSFW_Logo_V3.png new file mode 100644 index 0000000000000000000000000000000000000000..2ac710dd2069446e8f10108716f4c0e97bc961b9 GIT binary patch literal 137423 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

e=h(U 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 + + + + + + + + + + + + + + + + + + + + + + + fs fw + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + fs fw + + + + + + + + + + + + + fs fw + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logo/FSFW_Logo_V3_bw.png b/logo/FSFW_Logo_V3_bw.png new file mode 100644 index 0000000000000000000000000000000000000000..99b9b4f9608f4e53b86ff83747a114477004c9f1 GIT binary patch literal 13369 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 Date: Tue, 1 Dec 2020 18:15:09 +0100 Subject: [PATCH 17/22] replacing global pool with local pool DHB --- devicehandlers/DeviceHandlerBase.cpp | 39 ++++++++++++++++------------ devicehandlers/DeviceHandlerBase.h | 8 +++--- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index ccd5bebb..969c4688 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -16,6 +16,7 @@ #include +#include "../datapoollocal/LocalPoolVariable.h" object_id_t DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT; object_id_t DeviceHandlerBase::rawDataReceiverId = objects::NO_OBJECT; object_id_t DeviceHandlerBase::defaultFdirParentId = objects::NO_OBJECT; @@ -56,7 +57,7 @@ void DeviceHandlerBase::setHkDestination(object_id_t hkDestination) { } void DeviceHandlerBase::setThermalStateRequestPoolIds( - uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId) { + gp_id_t thermalStatePoolId, gp_id_t thermalRequestPoolId) { this->deviceThermalRequestPoolId = thermalStatePoolId; this->deviceThermalRequestPoolId = thermalRequestPoolId; } @@ -211,15 +212,15 @@ ReturnValue_t DeviceHandlerBase::initialize() { fillCommandAndReplyMap(); //Set temperature target state to NON_OP. - GlobDataSet mySet; - gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, - PoolVariableIF::VAR_WRITE); - mySet.read(); - thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; - mySet.commit(PoolVariableIF::VALID); + LocalPoolVar thermalRequest(deviceThermalRequestPoolId, nullptr, + pool_rwm_t::VAR_WRITE); + ReturnValue_t result = thermalRequest.read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; + thermalRequest.commit(PoolVariableIF::VALID); + } return RETURN_OK; - } void DeviceHandlerBase::decrementDeviceReplyMap() { @@ -508,14 +509,18 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) { Clock::getUptime(&timeoutStart); if (mode == MODE_OFF) { - GlobDataSet mySet; - gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, - PoolVariableIF::VAR_READ_WRITE); - mySet.read(); - if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { - thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; + LocalPoolVar heaterRequest(deviceThermalRequestPoolId, + nullptr, PoolVariableIF::VAR_READ_WRITE); + ReturnValue_t result = heaterRequest.read(); + if(heaterRequest == HasReturnvaluesIF::RETURN_OK) { + if (heaterRequest.value != + ThermalComponentIF::STATE_REQUEST_IGNORE) { + heaterRequest.value = ThermalComponentIF:: + STATE_REQUEST_NON_OPERATIONAL; + } + heaterRequest.commit(PoolVariableIF::VALID); } - mySet.commit(PoolVariableIF::VALID); + } changeHK(mode, submode, true); } @@ -1374,8 +1379,8 @@ bool DeviceHandlerBase::commandIsExecuting(DeviceCommandId_t commandId) { void DeviceHandlerBase::changeHK(Mode_t mode, Submode_t submode, bool enable) { } -void DeviceHandlerBase::setTaskIF(PeriodicTaskIF* task_){ - executingTask = task_; +void DeviceHandlerBase::setTaskIF(PeriodicTaskIF* task){ + executingTask = task; } // Default implementations empty. diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index 627a6423..f5d6ec2b 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -103,8 +103,8 @@ public: size_t cmdQueueSize = 20); void setHkDestination(object_id_t hkDestination); - void setThermalStateRequestPoolIds(uint32_t thermalStatePoolId, - uint32_t thermalRequestPoolId); + void setThermalStateRequestPoolIds(gp_id_t thermalStatePoolId, + gp_id_t thermalRequestPoolId); /** * @brief Helper function to ease device handler development. * This will instruct the transition to MODE_ON immediately @@ -699,14 +699,14 @@ protected: * * can be set to PoolVariableIF::NO_PARAMETER to deactivate thermal checking */ - uint32_t deviceThermalStatePoolId = PoolVariableIF::NO_PARAMETER; + gp_id_t deviceThermalStatePoolId; /** * this is the datapool variable with the thermal request of the device * * can be set to PoolVariableIF::NO_PARAMETER to deactivate thermal checking */ - uint32_t deviceThermalRequestPoolId = PoolVariableIF::NO_PARAMETER; + gp_id_t deviceThermalRequestPoolId; /** * Optional Error code. Can be set in doStartUp(), doShutDown() and From 3fb3039be530acdc21f41ca09dc7fc9d276827dc Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 18:16:26 +0100 Subject: [PATCH 18/22] minus 1 replacement --- devicehandlers/DeviceHandlerIF.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicehandlers/DeviceHandlerIF.h b/devicehandlers/DeviceHandlerIF.h index f64e1589..f09ddbb8 100644 --- a/devicehandlers/DeviceHandlerIF.h +++ b/devicehandlers/DeviceHandlerIF.h @@ -21,7 +21,7 @@ using DeviceCommandId_t = uint32_t; */ class DeviceHandlerIF { public: - static const DeviceCommandId_t NO_COMMAND = 0xffffffff; + static const DeviceCommandId_t NO_COMMAND = -1; static const uint8_t TRANSITION_MODE_CHILD_ACTION_MASK = 0x20; static const uint8_t TRANSITION_MODE_BASE_ACTION_MASK = 0x10; From fff928a191eced806e6f8aa9fa2e58d2c26b3565 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 18:29:00 +0100 Subject: [PATCH 19/22] bugfixes and improvements --- devicehandlers/DeviceHandlerBase.cpp | 45 ++++++++++++++++++---------- devicehandlers/DeviceHandlerBase.h | 20 +++++++++---- devicehandlers/DeviceHandlerIF.h | 3 ++ 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 969c4688..60929fcd 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -13,10 +13,11 @@ #include "../ipc/MessageQueueMessage.h" #include "../ipc/QueueFactory.h" #include "../subsystem/SubsystemBase.h" +#include "../datapoollocal/LocalPoolVariable.h" #include -#include "../datapoollocal/LocalPoolVariable.h" + object_id_t DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT; object_id_t DeviceHandlerBase::rawDataReceiverId = objects::NO_OBJECT; object_id_t DeviceHandlerBase::defaultFdirParentId = objects::NO_OBJECT; @@ -57,9 +58,9 @@ void DeviceHandlerBase::setHkDestination(object_id_t hkDestination) { } void DeviceHandlerBase::setThermalStateRequestPoolIds( - gp_id_t thermalStatePoolId, gp_id_t thermalRequestPoolId) { - this->deviceThermalRequestPoolId = thermalStatePoolId; - this->deviceThermalRequestPoolId = thermalRequestPoolId; + lp_id_t thermalStatePoolId, lp_id_t thermalRequestPoolId) { + this->deviceHeaterRequestPoolId = thermalStatePoolId; + this->deviceHeaterRequestPoolId = thermalRequestPoolId; } @@ -211,13 +212,17 @@ ReturnValue_t DeviceHandlerBase::initialize() { fillCommandAndReplyMap(); - //Set temperature target state to NON_OP. - LocalPoolVar thermalRequest(deviceThermalRequestPoolId, nullptr, - pool_rwm_t::VAR_WRITE); - ReturnValue_t result = thermalRequest.read(); - if(result == HasReturnvaluesIF::RETURN_OK) { - thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; - thermalRequest.commit(PoolVariableIF::VALID); + if(deviceHeaterRequestPoolId != localpool::INVALID_LPID) { + //Set temperature target state to NON_OP. + LocalPoolVar + heaterRequest(this, deviceHeaterRequestPoolId, nullptr, + pool_rwm_t::VAR_WRITE); + result = heaterRequest.read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + heaterRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; + heaterRequest.commit(PoolVariableIF::VALID); + } + } return RETURN_OK; @@ -509,10 +514,11 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) { Clock::getUptime(&timeoutStart); if (mode == MODE_OFF) { - LocalPoolVar heaterRequest(deviceThermalRequestPoolId, + LocalPoolVar + heaterRequest(this, deviceHeaterRequestPoolId, nullptr, PoolVariableIF::VAR_READ_WRITE); ReturnValue_t result = heaterRequest.read(); - if(heaterRequest == HasReturnvaluesIF::RETURN_OK) { + if(result == HasReturnvaluesIF::RETURN_OK) { if (heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) { heaterRequest.value = ThermalComponentIF:: @@ -987,7 +993,7 @@ ReturnValue_t DeviceHandlerBase::checkModeCommand(Mode_t commandedMode, GlobDataSet mySet; gp_uint8_t thermalState(deviceThermalStatePoolId, &mySet, PoolVariableIF::VAR_READ); - gp_uint8_t thermalRequest(deviceThermalRequestPoolId, &mySet, + gp_uint8_t thermalRequest(deviceHeaterRequestPoolId, &mySet, PoolVariableIF::VAR_READ); mySet.read(); if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { @@ -1015,7 +1021,7 @@ void DeviceHandlerBase::startTransition(Mode_t commandedMode, MODE_ON); triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); GlobDataSet mySet; - gp_int8_t thermalRequest(deviceThermalRequestPoolId, + gp_int8_t thermalRequest(deviceHeaterRequestPoolId, &mySet, PoolVariableIF::VAR_READ_WRITE); mySet.read(); if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { @@ -1393,6 +1399,15 @@ void DeviceHandlerBase::performOperationHook() { ReturnValue_t DeviceHandlerBase::initializeLocalDataPool( LocalDataPool &localDataPoolMap, LocalDataPoolManager& poolManager) { + if(deviceThermalStatePoolId != localpool::INVALID_LPID) { + localDataPoolMap.emplace(deviceThermalStatePoolId, + new PoolEntry); + } + + if(deviceHeaterRequestPoolId != localpool::INVALID_LPID) { + localDataPoolMap.emplace(deviceHeaterRequestPoolId, + new PoolEntry); + } return RETURN_OK; } diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index f5d6ec2b..b41e7407 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -103,8 +103,18 @@ public: size_t cmdQueueSize = 20); void setHkDestination(object_id_t hkDestination); - void setThermalStateRequestPoolIds(gp_id_t thermalStatePoolId, - gp_id_t thermalRequestPoolId); + + /** + * If the device handler is controlled by the FSFW thermal building blocks, + * this function should be called. The device handler will then take care + * of creating local pool entries for the device thermal state and device + * heating request. Custom local pool IDs can be assigned as well. + * @param thermalStatePoolId + * @param thermalRequestPoolId + */ + void setThermalStateRequestPoolIds( + lp_id_t thermalStatePoolId = localpool::INVALID_LPID - 1, + lp_id_t thermalRequestPoolId = localpool::INVALID_LPID - 2); /** * @brief Helper function to ease device handler development. * This will instruct the transition to MODE_ON immediately @@ -220,7 +230,7 @@ protected: * - If the device does not change the mode, the mode will be changed to * _MODE_POWER_DOWN, when the timeout (from getTransitionDelay()) * has passed. - * + * 0xffffffff * #transitionFailure can be set to a failure code indicating the reason * for a failed transition */ @@ -699,14 +709,14 @@ protected: * * can be set to PoolVariableIF::NO_PARAMETER to deactivate thermal checking */ - gp_id_t deviceThermalStatePoolId; + lp_id_t deviceThermalStatePoolId = localpool::INVALID_LPID; /** * this is the datapool variable with the thermal request of the device * * can be set to PoolVariableIF::NO_PARAMETER to deactivate thermal checking */ - gp_id_t deviceThermalRequestPoolId; + lp_id_t deviceHeaterRequestPoolId = localpool::INVALID_LPID; /** * Optional Error code. Can be set in doStartUp(), doShutDown() and diff --git a/devicehandlers/DeviceHandlerIF.h b/devicehandlers/DeviceHandlerIF.h index f09ddbb8..f1ea8801 100644 --- a/devicehandlers/DeviceHandlerIF.h +++ b/devicehandlers/DeviceHandlerIF.h @@ -26,6 +26,9 @@ public: static const uint8_t TRANSITION_MODE_CHILD_ACTION_MASK = 0x20; static const uint8_t TRANSITION_MODE_BASE_ACTION_MASK = 0x10; + using dh_heater_request_t = uint8_t; + using dh_thermal_state_t = int8_t; + /** * @brief This is the mode the device handler is in. * From 74b2830d9b2c612bff9467f063617028cbf309a8 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 18:55:53 +0100 Subject: [PATCH 20/22] using new device handler thermal set --- devicehandlers/DeviceHandlerBase.cpp | 56 ++++++++++++------------ devicehandlers/DeviceHandlerBase.h | 11 +++-- devicehandlers/DeviceHandlerIF.h | 9 ++++ devicehandlers/DeviceHandlerThermalSet.h | 44 +++++++++++++++++++ 4 files changed, 90 insertions(+), 30 deletions(-) create mode 100644 devicehandlers/DeviceHandlerThermalSet.h diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 60929fcd..609ba315 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -58,9 +58,12 @@ void DeviceHandlerBase::setHkDestination(object_id_t hkDestination) { } void DeviceHandlerBase::setThermalStateRequestPoolIds( - lp_id_t thermalStatePoolId, lp_id_t thermalRequestPoolId) { + lp_id_t thermalStatePoolId, lp_id_t heaterRequestPoolId, + uint32_t thermalSetId) { this->deviceHeaterRequestPoolId = thermalStatePoolId; - this->deviceHeaterRequestPoolId = thermalRequestPoolId; + this->deviceHeaterRequestPoolId = heaterRequestPoolId; + thermalSet = new DeviceHandlerThermalSet(this, thermalSetId, + thermalStatePoolId, heaterRequestPoolId); } @@ -212,15 +215,14 @@ ReturnValue_t DeviceHandlerBase::initialize() { fillCommandAndReplyMap(); - if(deviceHeaterRequestPoolId != localpool::INVALID_LPID) { + if(thermalSet != nullptr) { //Set temperature target state to NON_OP. - LocalPoolVar - heaterRequest(this, deviceHeaterRequestPoolId, nullptr, - pool_rwm_t::VAR_WRITE); - result = heaterRequest.read(); + ReturnValue_t result = thermalSet->read(); if(result == HasReturnvaluesIF::RETURN_OK) { - heaterRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; - heaterRequest.commit(PoolVariableIF::VALID); + thermalSet->heaterRequest.setReadWriteMode(pool_rwm_t::VAR_WRITE); + thermalSet->heaterRequest.value = + ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; + thermalSet->commit(PoolVariableIF::VALID); } } @@ -513,18 +515,17 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) { } Clock::getUptime(&timeoutStart); - if (mode == MODE_OFF) { - LocalPoolVar - heaterRequest(this, deviceHeaterRequestPoolId, - nullptr, PoolVariableIF::VAR_READ_WRITE); - ReturnValue_t result = heaterRequest.read(); + if (mode == MODE_OFF and thermalSet != nullptr) { + ReturnValue_t result = thermalSet->read(); if(result == HasReturnvaluesIF::RETURN_OK) { - if (heaterRequest.value != + thermalSet->heaterRequest.setReadWriteMode( + pool_rwm_t::VAR_READ_WRITE); + if (thermalSet->heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) { - heaterRequest.value = ThermalComponentIF:: + thermalSet->heaterRequest.value = ThermalComponentIF:: STATE_REQUEST_NON_OPERATIONAL; } - heaterRequest.commit(PoolVariableIF::VALID); + thermalSet->heaterRequest.commit(PoolVariableIF::VALID); } } @@ -989,17 +990,18 @@ ReturnValue_t DeviceHandlerBase::checkModeCommand(Mode_t commandedMode, } if ((commandedMode == MODE_ON) && (mode == MODE_OFF) - && (deviceThermalStatePoolId != PoolVariableIF::NO_PARAMETER)) { - GlobDataSet mySet; - gp_uint8_t thermalState(deviceThermalStatePoolId, &mySet, - PoolVariableIF::VAR_READ); - gp_uint8_t thermalRequest(deviceHeaterRequestPoolId, &mySet, - PoolVariableIF::VAR_READ); - mySet.read(); - if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { - if (!ThermalComponentIF::isOperational(thermalState)) { + && (deviceThermalStatePoolId != PoolVariableIF::NO_PARAMETER) + and thermalSet != nullptr) { + ReturnValue_t result = thermalSet->read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + thermalSet->thermalState.setReadWriteMode(pool_rwm_t::VAR_READ); + thermalSet->heaterRequest.setReadWriteMode(pool_rwm_t::VAR_READ); + if((thermalSet->heaterRequest.value != + ThermalComponentIF::STATE_REQUEST_IGNORE) and (not + ThermalComponentIF::isOperational( + thermalSet->thermalState.value))) { triggerEvent(ThermalComponentIF::TEMP_NOT_IN_OP_RANGE, - thermalState); + thermalSet->thermalState.value); return NON_OP_TEMPERATURE; } } diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index b41e7407..eb15f22a 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -4,6 +4,7 @@ #include "DeviceHandlerIF.h" #include "DeviceCommunicationIF.h" #include "DeviceHandlerFailureIsolation.h" +#include "DeviceHandlerThermalSet.h" #include "../objectmanager/SystemObject.h" #include "../tasks/ExecutableObjectIF.h" @@ -112,9 +113,11 @@ public: * @param thermalStatePoolId * @param thermalRequestPoolId */ - void setThermalStateRequestPoolIds( - lp_id_t thermalStatePoolId = localpool::INVALID_LPID - 1, - lp_id_t thermalRequestPoolId = localpool::INVALID_LPID - 2); + void setThermalStateRequestPoolIds(lp_id_t thermalStatePoolId = + DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID, + lp_id_t thermalRequestPoolId = + DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID, + uint32_t thermalSetId = DeviceHandlerIF::DEFAULT_THERMAL_SET_ID); /** * @brief Helper function to ease device handler development. * This will instruct the transition to MODE_ON immediately @@ -718,6 +721,8 @@ protected: */ lp_id_t deviceHeaterRequestPoolId = localpool::INVALID_LPID; + DeviceHandlerThermalSet* thermalSet = nullptr; + /** * Optional Error code. Can be set in doStartUp(), doShutDown() and * doTransition() to signal cause for Transition failure. diff --git a/devicehandlers/DeviceHandlerIF.h b/devicehandlers/DeviceHandlerIF.h index f1ea8801..a86a2b3a 100644 --- a/devicehandlers/DeviceHandlerIF.h +++ b/devicehandlers/DeviceHandlerIF.h @@ -4,6 +4,7 @@ #include "DeviceHandlerMessage.h" #include "../action/HasActionsIF.h" +#include "../datapoollocal/locPoolDefinitions.h" #include "../events/Event.h" #include "../modes/HasModesIF.h" #include "../ipc/MessageQueueSenderIF.h" @@ -151,6 +152,14 @@ public: NOTHING //!< Do nothing. }; + static constexpr uint32_t DEFAULT_THERMAL_SET_ID = sid_t::INVALID_SID - 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; + + /** * Default Destructor */ diff --git a/devicehandlers/DeviceHandlerThermalSet.h b/devicehandlers/DeviceHandlerThermalSet.h new file mode 100644 index 00000000..8e9f9c51 --- /dev/null +++ b/devicehandlers/DeviceHandlerThermalSet.h @@ -0,0 +1,44 @@ +#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_ +#define FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_ + +#include "DeviceHandlerIF.h" +#include "../datapoollocal/StaticLocalDataSet.h" +#include "../datapoollocal/LocalPoolVariable.h" + + +class DeviceHandlerThermalSet: public StaticLocalDataSet<2> { +public: + + DeviceHandlerThermalSet(HasLocalDataPoolIF* hkOwner, uint32_t setId = + DeviceHandlerIF::DEFAULT_THERMAL_SET_ID, + lp_id_t thermalStateId = + DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID, + lp_id_t heaterRequestId = + DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID): + DeviceHandlerThermalSet(hkOwner->getObjectId(), setId, + thermalStateId, heaterRequestId) {} + + DeviceHandlerThermalSet(object_id_t deviceHandler, uint32_t setId = + DeviceHandlerIF::DEFAULT_THERMAL_SET_ID, + lp_id_t thermalStateId = + DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID, + lp_id_t thermalStateRequestId = + DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID): + StaticLocalDataSet(sid_t(deviceHandler, setId)), + thermalStatePoolId(thermalStateId), + thermalStateRequestPoolId(thermalStateRequestId) {} + + const lp_id_t thermalStatePoolId; + const lp_id_t thermalStateRequestPoolId; + + lp_var_t thermalState = + lp_var_t( + thermalStatePoolId, sid.objectId, this); + lp_var_t heaterRequest = + lp_var_t( + thermalStateRequestPoolId, sid.objectId, this); +}; + + + +#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_ */ From e3de5ce7774b8571865ff6755056527912f0e7c2 Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 19:12:40 +0100 Subject: [PATCH 21/22] device handler changes complete --- devicehandlers/DeviceHandlerBase.cpp | 71 ++++++++++++------------ devicehandlers/DeviceHandlerBase.h | 24 +++----- devicehandlers/DeviceHandlerIF.h | 2 +- devicehandlers/DeviceHandlerThermalSet.h | 6 +- 4 files changed, 46 insertions(+), 57 deletions(-) diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 609ba315..d14a8e36 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -60,8 +60,6 @@ void DeviceHandlerBase::setHkDestination(object_id_t hkDestination) { void DeviceHandlerBase::setThermalStateRequestPoolIds( lp_id_t thermalStatePoolId, lp_id_t heaterRequestPoolId, uint32_t thermalSetId) { - this->deviceHeaterRequestPoolId = thermalStatePoolId; - this->deviceHeaterRequestPoolId = heaterRequestPoolId; thermalSet = new DeviceHandlerThermalSet(this, thermalSetId, thermalStatePoolId, heaterRequestPoolId); } @@ -217,9 +215,8 @@ ReturnValue_t DeviceHandlerBase::initialize() { if(thermalSet != nullptr) { //Set temperature target state to NON_OP. - ReturnValue_t result = thermalSet->read(); + result = thermalSet->read(); if(result == HasReturnvaluesIF::RETURN_OK) { - thermalSet->heaterRequest.setReadWriteMode(pool_rwm_t::VAR_WRITE); thermalSet->heaterRequest.value = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; thermalSet->commit(PoolVariableIF::VALID); @@ -518,8 +515,6 @@ void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) { if (mode == MODE_OFF and thermalSet != nullptr) { ReturnValue_t result = thermalSet->read(); if(result == HasReturnvaluesIF::RETURN_OK) { - thermalSet->heaterRequest.setReadWriteMode( - pool_rwm_t::VAR_READ_WRITE); if (thermalSet->heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) { thermalSet->heaterRequest.value = ThermalComponentIF:: @@ -990,12 +985,9 @@ ReturnValue_t DeviceHandlerBase::checkModeCommand(Mode_t commandedMode, } if ((commandedMode == MODE_ON) && (mode == MODE_OFF) - && (deviceThermalStatePoolId != PoolVariableIF::NO_PARAMETER) - and thermalSet != nullptr) { + and (thermalSet != nullptr)) { ReturnValue_t result = thermalSet->read(); if(result == HasReturnvaluesIF::RETURN_OK) { - thermalSet->thermalState.setReadWriteMode(pool_rwm_t::VAR_READ); - thermalSet->heaterRequest.setReadWriteMode(pool_rwm_t::VAR_READ); if((thermalSet->heaterRequest.value != ThermalComponentIF::STATE_REQUEST_IGNORE) and (not ThermalComponentIF::isOperational( @@ -1014,32 +1006,15 @@ void DeviceHandlerBase::startTransition(Mode_t commandedMode, Submode_t commandedSubmode) { switch (commandedMode) { case MODE_ON: - if (mode == MODE_OFF) { - transitionSourceMode = _MODE_POWER_DOWN; - transitionSourceSubMode = SUBMODE_NONE; - setMode(_MODE_POWER_ON, commandedSubmode); - //already set the delay for the child transition so we don't need to call it twice - childTransitionDelay = getTransitionDelayMs(_MODE_START_UP, - MODE_ON); - triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); - GlobDataSet mySet; - gp_int8_t thermalRequest(deviceHeaterRequestPoolId, - &mySet, PoolVariableIF::VAR_READ_WRITE); - mySet.read(); - if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { - thermalRequest = ThermalComponentIF::STATE_REQUEST_OPERATIONAL; - mySet.commit(PoolVariableIF::VALID); - } - } else { - setTransition(MODE_ON, commandedSubmode); - } + handleTransitionToOnMode(commandedMode, commandedSubmode); break; case MODE_OFF: if (mode == MODE_OFF) { triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); setMode(_MODE_POWER_DOWN, commandedSubmode); } else { - //already set the delay for the child transition so we don't need to call it twice + // already set the delay for the child transition + // so we don't need to call it twice childTransitionDelay = getTransitionDelayMs(mode, _MODE_POWER_DOWN); transitionSourceMode = _MODE_POWER_DOWN; transitionSourceSubMode = commandedSubmode; @@ -1065,6 +1040,33 @@ void DeviceHandlerBase::startTransition(Mode_t commandedMode, } } +void DeviceHandlerBase::handleTransitionToOnMode(Mode_t commandedMode, + Submode_t commandedSubmode) { + if (mode == MODE_OFF) { + transitionSourceMode = _MODE_POWER_DOWN; + transitionSourceSubMode = SUBMODE_NONE; + setMode(_MODE_POWER_ON, commandedSubmode); + // already set the delay for the child transition so we don't + // need to call it twice + childTransitionDelay = getTransitionDelayMs(_MODE_START_UP, + MODE_ON); + triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); + if(thermalSet != nullptr) { + ReturnValue_t result = thermalSet->read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + if(thermalSet->heaterRequest != + ThermalComponentIF::STATE_REQUEST_IGNORE) { + thermalSet->heaterRequest = + ThermalComponentIF::STATE_REQUEST_OPERATIONAL; + thermalSet->commit(); + } + } + } + } else { + setTransition(MODE_ON, commandedSubmode); + } +} + void DeviceHandlerBase::getMode(Mode_t* mode, Submode_t* submode) { *mode = this->mode; *submode = this->submode; @@ -1401,13 +1403,10 @@ void DeviceHandlerBase::performOperationHook() { ReturnValue_t DeviceHandlerBase::initializeLocalDataPool( LocalDataPool &localDataPoolMap, LocalDataPoolManager& poolManager) { - if(deviceThermalStatePoolId != localpool::INVALID_LPID) { - localDataPoolMap.emplace(deviceThermalStatePoolId, + if(thermalSet != nullptr) { + localDataPoolMap.emplace(thermalSet->thermalStatePoolId, new PoolEntry); - } - - if(deviceHeaterRequestPoolId != localpool::INVALID_LPID) { - localDataPoolMap.emplace(deviceHeaterRequestPoolId, + localDataPoolMap.emplace(thermalSet->heaterRequestPoolId, new PoolEntry); } return RETURN_OK; diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index eb15f22a..4deef763 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -107,9 +107,10 @@ public: /** * If the device handler is controlled by the FSFW thermal building blocks, - * this function should be called. The device handler will then take care - * of creating local pool entries for the device thermal state and device - * heating request. Custom local pool IDs can be assigned as well. + * this function should be called to initialize all required components. + * The device handler will then take care of creating local pool entries + * for the device thermal state and device heating request. + * Custom local pool IDs can be assigned as well. * @param thermalStatePoolId * @param thermalRequestPoolId */ @@ -707,20 +708,6 @@ protected: //! and to send replies. MessageQueueIF* commandQueue = nullptr; - /** - * this is the datapool variable with the thermal state of the device - * - * can be set to PoolVariableIF::NO_PARAMETER to deactivate thermal checking - */ - lp_id_t deviceThermalStatePoolId = localpool::INVALID_LPID; - - /** - * this is the datapool variable with the thermal request of the device - * - * can be set to PoolVariableIF::NO_PARAMETER to deactivate thermal checking - */ - lp_id_t deviceHeaterRequestPoolId = localpool::INVALID_LPID; - DeviceHandlerThermalSet* thermalSet = nullptr; /** @@ -1223,6 +1210,9 @@ private: void parseReply(const uint8_t* receivedData, size_t receivedDataLen); + + void handleTransitionToOnMode(Mode_t commandedMode, + Submode_t commandedSubmode); }; #endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */ diff --git a/devicehandlers/DeviceHandlerIF.h b/devicehandlers/DeviceHandlerIF.h index a86a2b3a..4cda99d4 100644 --- a/devicehandlers/DeviceHandlerIF.h +++ b/devicehandlers/DeviceHandlerIF.h @@ -152,7 +152,7 @@ public: NOTHING //!< Do nothing. }; - static constexpr uint32_t DEFAULT_THERMAL_SET_ID = sid_t::INVALID_SID - 1; + static constexpr uint32_t DEFAULT_THERMAL_SET_ID = sid_t::INVALID_SET_ID - 1; static constexpr lp_id_t DEFAULT_THERMAL_STATE_POOL_ID = localpool::INVALID_LPID - 2; diff --git a/devicehandlers/DeviceHandlerThermalSet.h b/devicehandlers/DeviceHandlerThermalSet.h index 8e9f9c51..239012e2 100644 --- a/devicehandlers/DeviceHandlerThermalSet.h +++ b/devicehandlers/DeviceHandlerThermalSet.h @@ -26,17 +26,17 @@ public: DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID): StaticLocalDataSet(sid_t(deviceHandler, setId)), thermalStatePoolId(thermalStateId), - thermalStateRequestPoolId(thermalStateRequestId) {} + heaterRequestPoolId(thermalStateRequestId) {} const lp_id_t thermalStatePoolId; - const lp_id_t thermalStateRequestPoolId; + const lp_id_t heaterRequestPoolId; lp_var_t thermalState = lp_var_t( thermalStatePoolId, sid.objectId, this); lp_var_t heaterRequest = lp_var_t( - thermalStateRequestPoolId, sid.objectId, this); + heaterRequestPoolId, sid.objectId, this); }; From 4d76bf24b581aaeba5c2e276f4d09c90c806215e Mon Sep 17 00:00:00 2001 From: "Robin.Mueller" Date: Tue, 1 Dec 2020 19:16:19 +0100 Subject: [PATCH 22/22] fsfw compiles without datapoolglob folder --- .../datapoolglob}/ControllerSet.cpp | 0 .../datapoolglob}/ControllerSet.h | 0 .../datapoolglob}/DataPoolAdmin.cpp | 0 .../datapoolglob}/DataPoolAdmin.h | 0 .../datapoolglob}/DataPoolParameterWrapper.cpp | 0 .../datapoolglob}/DataPoolParameterWrapper.h | 0 .../datapoolglob}/GlobalDataPool.cpp | 0 .../datapoolglob}/GlobalDataPool.h | 0 .../datapoolglob}/GlobalDataSet.cpp | 0 .../datapoolglob}/GlobalDataSet.h | 0 .../datapoolglob}/GlobalPoolVariable.h | 0 .../datapoolglob}/GlobalPoolVariable.tpp | 0 .../datapoolglob}/GlobalPoolVector.h | 0 .../datapoolglob}/GlobalPoolVector.tpp | 0 {datapoolglob => archive/datapoolglob}/PIDReader.h | 0 .../datapoolglob}/PIDReaderList.h | 0 .../datapoolglob}/PoolRawAccess.cpp | 0 .../datapoolglob}/PoolRawAccess.h | 0 devicehandlers/DeviceHandlerBase.cpp | 12 ++++++------ internalError/InternalErrorReporter.cpp | 2 -- 20 files changed, 6 insertions(+), 8 deletions(-) rename {datapoolglob => archive/datapoolglob}/ControllerSet.cpp (100%) rename {datapoolglob => archive/datapoolglob}/ControllerSet.h (100%) rename {datapoolglob => archive/datapoolglob}/DataPoolAdmin.cpp (100%) rename {datapoolglob => archive/datapoolglob}/DataPoolAdmin.h (100%) rename {datapoolglob => archive/datapoolglob}/DataPoolParameterWrapper.cpp (100%) rename {datapoolglob => archive/datapoolglob}/DataPoolParameterWrapper.h (100%) rename {datapoolglob => archive/datapoolglob}/GlobalDataPool.cpp (100%) rename {datapoolglob => archive/datapoolglob}/GlobalDataPool.h (100%) rename {datapoolglob => archive/datapoolglob}/GlobalDataSet.cpp (100%) rename {datapoolglob => archive/datapoolglob}/GlobalDataSet.h (100%) rename {datapoolglob => archive/datapoolglob}/GlobalPoolVariable.h (100%) rename {datapoolglob => archive/datapoolglob}/GlobalPoolVariable.tpp (100%) rename {datapoolglob => archive/datapoolglob}/GlobalPoolVector.h (100%) rename {datapoolglob => archive/datapoolglob}/GlobalPoolVector.tpp (100%) rename {datapoolglob => archive/datapoolglob}/PIDReader.h (100%) rename {datapoolglob => archive/datapoolglob}/PIDReaderList.h (100%) rename {datapoolglob => archive/datapoolglob}/PoolRawAccess.cpp (100%) rename {datapoolglob => archive/datapoolglob}/PoolRawAccess.h (100%) diff --git a/datapoolglob/ControllerSet.cpp b/archive/datapoolglob/ControllerSet.cpp similarity index 100% rename from datapoolglob/ControllerSet.cpp rename to archive/datapoolglob/ControllerSet.cpp diff --git a/datapoolglob/ControllerSet.h b/archive/datapoolglob/ControllerSet.h similarity index 100% rename from datapoolglob/ControllerSet.h rename to archive/datapoolglob/ControllerSet.h diff --git a/datapoolglob/DataPoolAdmin.cpp b/archive/datapoolglob/DataPoolAdmin.cpp similarity index 100% rename from datapoolglob/DataPoolAdmin.cpp rename to archive/datapoolglob/DataPoolAdmin.cpp diff --git a/datapoolglob/DataPoolAdmin.h b/archive/datapoolglob/DataPoolAdmin.h similarity index 100% rename from datapoolglob/DataPoolAdmin.h rename to archive/datapoolglob/DataPoolAdmin.h diff --git a/datapoolglob/DataPoolParameterWrapper.cpp b/archive/datapoolglob/DataPoolParameterWrapper.cpp similarity index 100% rename from datapoolglob/DataPoolParameterWrapper.cpp rename to archive/datapoolglob/DataPoolParameterWrapper.cpp diff --git a/datapoolglob/DataPoolParameterWrapper.h b/archive/datapoolglob/DataPoolParameterWrapper.h similarity index 100% rename from datapoolglob/DataPoolParameterWrapper.h rename to archive/datapoolglob/DataPoolParameterWrapper.h diff --git a/datapoolglob/GlobalDataPool.cpp b/archive/datapoolglob/GlobalDataPool.cpp similarity index 100% rename from datapoolglob/GlobalDataPool.cpp rename to archive/datapoolglob/GlobalDataPool.cpp diff --git a/datapoolglob/GlobalDataPool.h b/archive/datapoolglob/GlobalDataPool.h similarity index 100% rename from datapoolglob/GlobalDataPool.h rename to archive/datapoolglob/GlobalDataPool.h diff --git a/datapoolglob/GlobalDataSet.cpp b/archive/datapoolglob/GlobalDataSet.cpp similarity index 100% rename from datapoolglob/GlobalDataSet.cpp rename to archive/datapoolglob/GlobalDataSet.cpp diff --git a/datapoolglob/GlobalDataSet.h b/archive/datapoolglob/GlobalDataSet.h similarity index 100% rename from datapoolglob/GlobalDataSet.h rename to archive/datapoolglob/GlobalDataSet.h diff --git a/datapoolglob/GlobalPoolVariable.h b/archive/datapoolglob/GlobalPoolVariable.h similarity index 100% rename from datapoolglob/GlobalPoolVariable.h rename to archive/datapoolglob/GlobalPoolVariable.h diff --git a/datapoolglob/GlobalPoolVariable.tpp b/archive/datapoolglob/GlobalPoolVariable.tpp similarity index 100% rename from datapoolglob/GlobalPoolVariable.tpp rename to archive/datapoolglob/GlobalPoolVariable.tpp diff --git a/datapoolglob/GlobalPoolVector.h b/archive/datapoolglob/GlobalPoolVector.h similarity index 100% rename from datapoolglob/GlobalPoolVector.h rename to archive/datapoolglob/GlobalPoolVector.h diff --git a/datapoolglob/GlobalPoolVector.tpp b/archive/datapoolglob/GlobalPoolVector.tpp similarity index 100% rename from datapoolglob/GlobalPoolVector.tpp rename to archive/datapoolglob/GlobalPoolVector.tpp diff --git a/datapoolglob/PIDReader.h b/archive/datapoolglob/PIDReader.h similarity index 100% rename from datapoolglob/PIDReader.h rename to archive/datapoolglob/PIDReader.h diff --git a/datapoolglob/PIDReaderList.h b/archive/datapoolglob/PIDReaderList.h similarity index 100% rename from datapoolglob/PIDReaderList.h rename to archive/datapoolglob/PIDReaderList.h diff --git a/datapoolglob/PoolRawAccess.cpp b/archive/datapoolglob/PoolRawAccess.cpp similarity index 100% rename from datapoolglob/PoolRawAccess.cpp rename to archive/datapoolglob/PoolRawAccess.cpp diff --git a/datapoolglob/PoolRawAccess.h b/archive/datapoolglob/PoolRawAccess.h similarity index 100% rename from datapoolglob/PoolRawAccess.h rename to archive/datapoolglob/PoolRawAccess.h diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index d14a8e36..4bb7ed56 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -3,8 +3,6 @@ #include "DeviceTmReportingWrapper.h" #include "../serviceinterface/ServiceInterfaceStream.h" -#include "../datapoolglob/GlobalDataSet.h" -#include "../datapoolglob/GlobalPoolVariable.h" #include "../objectmanager/ObjectManager.h" #include "../storagemanager/StorageManagerIF.h" #include "../thermal/ThermalComponentIF.h" @@ -1239,10 +1237,12 @@ void DeviceHandlerBase::handleDeviceTM(SerializeIF* data, } } //Try to cast to GlobDataSet and commit data. - if (!neverInDataPool) { - GlobDataSet* dataSet = dynamic_cast(data); - if (dataSet != NULL) { - dataSet->commit(PoolVariableIF::VALID); + if (not neverInDataPool) { + LocalPoolDataSetBase* dataSet = + dynamic_cast(data); + if (dataSet != nullptr) { + dataSet->setValidity(true, true); + dataSet->commit(); } } } diff --git a/internalError/InternalErrorReporter.cpp b/internalError/InternalErrorReporter.cpp index 22e2c38c..8d5c792b 100644 --- a/internalError/InternalErrorReporter.cpp +++ b/internalError/InternalErrorReporter.cpp @@ -1,8 +1,6 @@ #include "InternalErrorReporter.h" #include "../ipc/QueueFactory.h" -#include "../datapoolglob/GlobalDataSet.h" -#include "../datapoolglob/GlobalPoolVariable.h" #include "../ipc/MutexFactory.h" #include "../serviceinterface/ServiceInterfaceStream.h"