diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ae6f59fd..acd56f3ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,26 +9,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). # [unreleased] # [v6.0.0] 2023-02-10 - - - - - - - - - - - - - - - - - - - - ## Fixes @@ -67,6 +47,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `TcpTmTcServer.cpp`: The server was actually not able to handle CCSDS packets which were clumped together. This has been fixed now. PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/673 +- `CServiceHealthCommanding`: Add announce all health info implementation + PR: https://egit.irs.uni-stuttgart.de/eive/fsfw/pulls/122 - various fixes related to linux Unittests and memory leaks PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/715 - small fix to allow teardown handling @@ -133,6 +115,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Changes +- `CService201HealthCommanding` renamed to `CServiceHealthCommanding`, + service ID customizable now. `CServiceHealthCommanding` expects configuration struct + `HealthServiceCfg` now + PR: https://egit.irs.uni-stuttgart.de/eive/fsfw/pulls/122 - `AcceptsTelemetryIF`: `getReportReceptionQueue` is const now PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/712 - Moved some container returnvalues to dedicated header and namespace diff --git a/src/fsfw/health/HealthTable.h b/src/fsfw/health/HealthTable.h index 076228c11..7b42dfbad 100644 --- a/src/fsfw/health/HealthTable.h +++ b/src/fsfw/health/HealthTable.h @@ -8,6 +8,8 @@ #include "HealthTableIF.h" class HealthTable : public HealthTableIF, public SystemObject { + friend class CServiceHealthCommanding; + public: explicit HealthTable(object_id_t objectid); ~HealthTable() override; diff --git a/src/fsfw/pus/CMakeLists.txt b/src/fsfw/pus/CMakeLists.txt index 35b35beac..7c5b340c3 100644 --- a/src/fsfw/pus/CMakeLists.txt +++ b/src/fsfw/pus/CMakeLists.txt @@ -9,4 +9,4 @@ target_sources( Service17Test.cpp Service20ParameterManagement.cpp CService200ModeCommanding.cpp - CService201HealthCommanding.cpp) + CServiceHealthCommanding.cpp) diff --git a/src/fsfw/pus/CService201HealthCommanding.cpp b/src/fsfw/pus/CService201HealthCommanding.cpp deleted file mode 100644 index bf21c5bdc..000000000 --- a/src/fsfw/pus/CService201HealthCommanding.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "fsfw/pus/CService201HealthCommanding.h" - -#include "fsfw/health/HasHealthIF.h" -#include "fsfw/health/HealthMessage.h" -#include "fsfw/objectmanager/ObjectManager.h" -#include "fsfw/pus/servicepackets/Service201Packets.h" -#include "fsfw/serviceinterface/ServiceInterface.h" - -CService201HealthCommanding::CService201HealthCommanding(object_id_t objectId, uint16_t apid, - uint8_t serviceId, - uint8_t numParallelCommands, - uint16_t commandTimeoutSeconds) - : CommandingServiceBase(objectId, apid, "PUS 201 Health MGMT", serviceId, numParallelCommands, - commandTimeoutSeconds) {} - -ReturnValue_t CService201HealthCommanding::isValidSubservice(uint8_t subservice) { - switch (subservice) { - case (Subservice::COMMAND_SET_HEALTH): - case (Subservice::COMMAND_ANNOUNCE_HEALTH): - case (Subservice::COMMAND_ANNOUNCE_HEALTH_ALL): - return returnvalue::OK; - default: -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "Invalid Subservice" << std::endl; -#endif - return AcceptsTelecommandsIF::INVALID_SUBSERVICE; - } -} - -ReturnValue_t CService201HealthCommanding::getMessageQueueAndObject(uint8_t subservice, - const uint8_t *tcData, - size_t tcDataLen, - MessageQueueId_t *id, - object_id_t *objectId) { - if (tcDataLen < sizeof(object_id_t)) { - return CommandingServiceBase::INVALID_TC; - } - SerializeAdapter::deSerialize(objectId, &tcData, &tcDataLen, SerializeIF::Endianness::BIG); - - return checkInterfaceAndAcquireMessageQueue(id, objectId); -} - -ReturnValue_t CService201HealthCommanding::checkInterfaceAndAcquireMessageQueue( - MessageQueueId_t *messageQueueToSet, const object_id_t *objectId) { - auto *destination = ObjectManager::instance()->get(*objectId); - if (destination == nullptr) { - return CommandingServiceBase::INVALID_OBJECT; - } - - *messageQueueToSet = destination->getCommandQueue(); - return returnvalue::OK; -} - -ReturnValue_t CService201HealthCommanding::prepareCommand(CommandMessage *message, - uint8_t subservice, const uint8_t *tcData, - size_t tcDataLen, uint32_t *state, - object_id_t objectId) { - ReturnValue_t result = returnvalue::OK; - switch (subservice) { - case (Subservice::COMMAND_SET_HEALTH): { - HealthSetCommand healthCommand; - result = healthCommand.deSerialize(&tcData, &tcDataLen, SerializeIF::Endianness::BIG); - if (result != returnvalue::OK) { - break; - } - HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_SET, - healthCommand.getHealth()); - break; - } - case (Subservice::COMMAND_ANNOUNCE_HEALTH): { - HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_ANNOUNCE); - break; - } - case (Subservice::COMMAND_ANNOUNCE_HEALTH_ALL): { - HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_ANNOUNCE_ALL); - break; - } - default: { - // Should never happen, subservice was already checked - result = returnvalue::FAILED; - } - } - return result; -} - -ReturnValue_t CService201HealthCommanding::handleReply(const CommandMessage *reply, - Command_t previousCommand, uint32_t *state, - CommandMessage *optionalNextCommand, - object_id_t objectId, bool *isStep) { - Command_t replyId = reply->getCommand(); - if (replyId == HealthMessage::REPLY_HEALTH_SET) { - return EXECUTION_COMPLETE; - } else if (replyId == CommandMessageIF::REPLY_REJECTED) { - return reply->getReplyRejectedReason(); - } - return CommandingServiceBase::INVALID_REPLY; -} - -// Not used for now, health state already reported by event -[[maybe_unused]] ReturnValue_t CService201HealthCommanding::prepareHealthSetReply( - const CommandMessage *reply) { - auto health = static_cast(HealthMessage::getHealth(reply)); - auto oldHealth = static_cast(HealthMessage::getOldHealth(reply)); - HealthSetReply healthSetReply(health, oldHealth); - return sendTmPacket(Subservice::REPLY_HEALTH_SET, healthSetReply); -} diff --git a/src/fsfw/pus/CServiceHealthCommanding.cpp b/src/fsfw/pus/CServiceHealthCommanding.cpp new file mode 100644 index 000000000..8efa3287f --- /dev/null +++ b/src/fsfw/pus/CServiceHealthCommanding.cpp @@ -0,0 +1,158 @@ +#include +#include + +#include "fsfw/health/HasHealthIF.h" +#include "fsfw/health/HealthMessage.h" +#include "fsfw/objectmanager/ObjectManager.h" +#include "fsfw/pus/servicepackets/Service201Packets.h" +#include "fsfw/serviceinterface/ServiceInterface.h" + +CServiceHealthCommanding::CServiceHealthCommanding(HealthServiceCfg args) + : CommandingServiceBase(args.objectId, args.apid, "PUS 201 Health MGMT", args.service, + args.numParallelCommands, args.commandTimeoutSeconds), + healthTableId(args.table), + maxNumHealthInfoPerCycle(args.maxNumHealthInfoPerCycle) {} + +ReturnValue_t CServiceHealthCommanding::initialize() { + ReturnValue_t result = CommandingServiceBase::initialize(); + if (result != returnvalue::OK) { + return result; + } + + healthTable = ObjectManager::instance()->get(healthTableId); + if(healthTable == nullptr) { + return returnvalue::FAILED; + } + + return returnvalue::OK; +} + +ReturnValue_t CServiceHealthCommanding::isValidSubservice(uint8_t subservice) { + switch (subservice) { + case (Subservice::COMMAND_SET_HEALTH): + case (Subservice::COMMAND_ANNOUNCE_HEALTH): + case (Subservice::COMMAND_ANNOUNCE_HEALTH_ALL): + return returnvalue::OK; + default: +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "Invalid Subservice" << std::endl; +#endif + return AcceptsTelecommandsIF::INVALID_SUBSERVICE; + } +} + +ReturnValue_t CServiceHealthCommanding::getMessageQueueAndObject(uint8_t subservice, + const uint8_t *tcData, + size_t tcDataLen, + MessageQueueId_t *id, + object_id_t *objectId) { + switch (subservice) { + case (Subservice::COMMAND_SET_HEALTH): + case (Subservice::COMMAND_ANNOUNCE_HEALTH): { + if (tcDataLen < sizeof(object_id_t)) { + return CommandingServiceBase::INVALID_TC; + } + SerializeAdapter::deSerialize(objectId, &tcData, &tcDataLen, SerializeIF::Endianness::BIG); + + return checkInterfaceAndAcquireMessageQueue(id, objectId); + } + case (Subservice::COMMAND_ANNOUNCE_HEALTH_ALL): { + return returnvalue::OK; + } + default: { + return returnvalue::FAILED; + } + } +} + +ReturnValue_t CServiceHealthCommanding::checkInterfaceAndAcquireMessageQueue( + MessageQueueId_t *messageQueueToSet, const object_id_t *objectId) { + auto *destination = ObjectManager::instance()->get(*objectId); + if (destination == nullptr) { + return CommandingServiceBase::INVALID_OBJECT; + } + + *messageQueueToSet = destination->getCommandQueue(); + return returnvalue::OK; +} + +ReturnValue_t CServiceHealthCommanding::prepareCommand(CommandMessage *message, uint8_t subservice, + const uint8_t *tcData, size_t tcDataLen, + uint32_t *state, object_id_t objectId) { + ReturnValue_t result = returnvalue::OK; + switch (subservice) { + case (Subservice::COMMAND_SET_HEALTH): { + HealthSetCommand healthCommand; + result = healthCommand.deSerialize(&tcData, &tcDataLen, SerializeIF::Endianness::BIG); + if (result != returnvalue::OK) { + break; + } + HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_SET, + healthCommand.getHealth()); + break; + } + case (Subservice::COMMAND_ANNOUNCE_HEALTH): { + HealthMessage::setHealthMessage(message, HealthMessage::HEALTH_ANNOUNCE); + break; + } + case (Subservice::COMMAND_ANNOUNCE_HEALTH_ALL): { + ReturnValue_t result = iterateHealthTable(true); + if (result == returnvalue::OK) { + reportAllHealth = true; + return EXECUTION_COMPLETE; + } + return result; + } + default: { + // Should never happen, subservice was already checked + result = returnvalue::FAILED; + } + } + return result; +} + +ReturnValue_t CServiceHealthCommanding::handleReply(const CommandMessage *reply, + Command_t previousCommand, uint32_t *state, + CommandMessage *optionalNextCommand, + object_id_t objectId, bool *isStep) { + Command_t replyId = reply->getCommand(); + if (replyId == HealthMessage::REPLY_HEALTH_SET) { + return EXECUTION_COMPLETE; + } else if (replyId == CommandMessageIF::REPLY_REJECTED) { + return reply->getReplyRejectedReason(); + } + return CommandingServiceBase::INVALID_REPLY; +} + +void CServiceHealthCommanding::doPeriodicOperation() { + if (reportAllHealth) { + for (uint8_t i = 0; i < maxNumHealthInfoPerCycle; i++) { + ReturnValue_t result = iterateHealthTable(false); + if (result != returnvalue::OK) { + reportAllHealth = false; + break; + } + } + } +} + +// Not used for now, health state already reported by event +[[maybe_unused]] ReturnValue_t CServiceHealthCommanding::prepareHealthSetReply( + const CommandMessage *reply) { + auto health = static_cast(HealthMessage::getHealth(reply)); + auto oldHealth = static_cast(HealthMessage::getOldHealth(reply)); + HealthSetReply healthSetReply(health, oldHealth); + return sendTmPacket(Subservice::REPLY_HEALTH_SET, healthSetReply); +} + +ReturnValue_t CServiceHealthCommanding::iterateHealthTable(bool reset) { + std::pair pair; + + ReturnValue_t result = healthTable->iterate(&pair, reset); + if (result != returnvalue::OK) { + return result; + } else { + EventManagerIF::triggerEvent(pair.first, HasHealthIF::HEALTH_INFO, pair.second, pair.second); + return returnvalue::OK; + } +} diff --git a/src/fsfw/pus/CService201HealthCommanding.h b/src/fsfw/pus/CServiceHealthCommanding.h similarity index 70% rename from src/fsfw/pus/CService201HealthCommanding.h rename to src/fsfw/pus/CServiceHealthCommanding.h index 71b7caa05..2cc16589f 100644 --- a/src/fsfw/pus/CService201HealthCommanding.h +++ b/src/fsfw/pus/CServiceHealthCommanding.h @@ -1,8 +1,26 @@ #ifndef FSFW_PUS_CSERVICE201HEALTHCOMMANDING_H_ #define FSFW_PUS_CSERVICE201HEALTHCOMMANDING_H_ +#include + #include "fsfw/tmtcservices/CommandingServiceBase.h" +struct HealthServiceCfg { + HealthServiceCfg(object_id_t objectId, uint16_t apid, object_id_t healthTable, + uint16_t maxNumHealthInfoPerCycle) + : objectId(objectId), + apid(apid), + table(healthTable), + maxNumHealthInfoPerCycle(maxNumHealthInfoPerCycle) {} + object_id_t objectId; + uint16_t apid; + object_id_t table; + uint16_t maxNumHealthInfoPerCycle; + uint8_t service = 201; + uint8_t numParallelCommands = 4; + uint16_t commandTimeoutSeconds = 60; +}; + /** * @brief Custom PUS service to set health of all objects * implementing hasHealthIF. @@ -17,11 +35,12 @@ * child class like this service * */ -class CService201HealthCommanding : public CommandingServiceBase { +class CServiceHealthCommanding : public CommandingServiceBase { public: - CService201HealthCommanding(object_id_t objectId, uint16_t apid, uint8_t serviceId, - uint8_t numParallelCommands = 4, uint16_t commandTimeoutSeconds = 60); - ~CService201HealthCommanding() override = default; + CServiceHealthCommanding(HealthServiceCfg args); + ~CServiceHealthCommanding() override = default; + + ReturnValue_t initialize() override; protected: /* CSB abstract function implementations */ @@ -37,7 +56,14 @@ class CService201HealthCommanding : public CommandingServiceBase { CommandMessage *optionalNextCommand, object_id_t objectId, bool *isStep) override; + void doPeriodicOperation() override; + private: + const object_id_t healthTableId; + HealthTable *healthTable; + uint16_t maxNumHealthInfoPerCycle = 0; + bool reportAllHealth = false; + ReturnValue_t iterateHealthTable(bool reset); static ReturnValue_t checkInterfaceAndAcquireMessageQueue(MessageQueueId_t *MessageQueueToSet, const object_id_t *objectId);