diff --git a/Ref/Top/RefPackets.fppi b/Ref/Top/RefPackets.fppi index 1360f0490b0..1c1c850c5a5 100644 --- a/Ref/Top/RefPackets.fppi +++ b/Ref/Top/RefPackets.fppi @@ -18,7 +18,8 @@ telemetry packets RefPackets { ComCcsds.commsBufferManager.TotalBuffs ComCcsds.commsBufferManager.CurrBuffs ComCcsds.commsBufferManager.HiBuffs - #ComCcsds.tlmSend.SendLevel + #ComCcsds.tlmSend.GroupConfigs + #ComCcsds.tlmSend.SectionEnabled Ref.rateGroup1Comp.RgMaxTime Ref.rateGroup2Comp.RgMaxTime diff --git a/Svc/Subtopologies/CdhCore/CdhCoreConfig/CdhCoreTlmConfig.fpp b/Svc/Subtopologies/CdhCore/CdhCoreConfig/CdhCoreTlmConfig.fpp index bb69471cdb4..40d6d47bdc9 100644 --- a/Svc/Subtopologies/CdhCore/CdhCoreConfig/CdhCoreTlmConfig.fpp +++ b/Svc/Subtopologies/CdhCore/CdhCoreConfig/CdhCoreTlmConfig.fpp @@ -6,11 +6,11 @@ module CdhCore{ priority CdhCoreConfig.Priorities.tlmSend \ # Uncomment the following block and comment the above block to use TlmPacketizer instead of TlmChan - #instance tlmSend: Svc.TlmPacketizer base id CdhCoreConfig.BASE_ID + 0x06000 \ + # instance tlmSend: Svc.TlmPacketizer base id CdhCoreConfig.BASE_ID + 0x06000 \ # queue size CdhCoreConfig.QueueSizes.tlmSend \ # stack size CdhCoreConfig.StackSizes.tlmSend \ # priority CdhCoreConfig.Priorities.tlmSend \ - #{ + # { # # NOTE: The Name Ref is specific to the Reference deployment, Ref # # This name will need to be updated if wishing to use this in a custom deployment # phase Fpp.ToCpp.Phases.configComponents """ @@ -20,5 +20,5 @@ module CdhCore{ # 1 # ); # """ - #} + # } } diff --git a/Svc/TlmPacketizer/TlmPacketizer.cpp b/Svc/TlmPacketizer/TlmPacketizer.cpp index 484cf3ca56e..f8b42c2d361 100644 --- a/Svc/TlmPacketizer/TlmPacketizer.cpp +++ b/Svc/TlmPacketizer/TlmPacketizer.cpp @@ -20,7 +20,7 @@ namespace Svc { // ---------------------------------------------------------------------- TlmPacketizer ::TlmPacketizer(const char* const compName) - : TlmPacketizerComponentBase(compName), m_numPackets(0), m_configured(false), m_startLevel(0), m_maxLevel(0) { + : TlmPacketizerComponentBase(compName), m_numPackets(0), m_configured(false) { // clear slot pointers for (FwChanIdType entry = 0; entry < TLMPACKETIZER_NUM_TLM_HASH_SLOTS; entry++) { this->m_tlmEntries.slots[entry] = nullptr; @@ -43,9 +43,13 @@ TlmPacketizer ::TlmPacketizer(const char* const compName) // clear packet buffers for (FwChanIdType buffer = 0; buffer < MAX_PACKETIZER_PACKETS; buffer++) { this->m_fillBuffers[buffer].updated = false; - this->m_fillBuffers[buffer].requested = false; this->m_sendBuffers[buffer].updated = false; } + + // enable sections + for (FwIndexType section = 0; section < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS; section++) { + this->m_sectionEnabled[section] = Fw::Enabled::ENABLED; + } } TlmPacketizer ::~TlmPacketizer() {} @@ -58,6 +62,7 @@ void TlmPacketizer::setPacketList(const TlmPacketizerPacketList& packetList, FW_ASSERT(packetList.numEntries <= MAX_PACKETIZER_PACKETS, static_cast(packetList.numEntries)); // validate packet sizes against maximum com buffer size and populate hash // table + FwChanIdType maxLevel = 0; for (FwChanIdType pktEntry = 0; pktEntry < packetList.numEntries; pktEntry++) { // Initial size is packetized telemetry descriptor + size of time tag + sizeof packet ID FwSizeType packetLen = @@ -103,13 +108,40 @@ void TlmPacketizer::setPacketList(const TlmPacketizerPacketList& packetList, // save level this->m_fillBuffers[pktEntry].level = packetList.list[pktEntry]->level; // store max level - if (packetList.list[pktEntry]->level > this->m_maxLevel) { - this->m_maxLevel = packetList.list[pktEntry]->level; + if (packetList.list[pktEntry]->level > maxLevel) { + maxLevel = packetList.list[pktEntry]->level; } - // save start level - this->m_startLevel = startLevel; } // end packet list + FW_ASSERT(maxLevel <= MAX_CONFIGURABLE_TLMPACKETIZER_GROUP, static_cast(maxLevel)); + + // Enable and set group configurations + for (FwIndexType section = 0; section < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS; section++) { + TlmPacketizer_RateLogic startRateLogic; + switch (PACKET_UPDATE_MODE) { + case PACKET_UPDATE_ON_CHANGE: + startRateLogic = TlmPacketizer_RateLogic::ON_CHANGE_MIN; + break; + case PACKET_UPDATE_ALWAYS: + for (PktSendCounters& pkt : this->m_packetFlags[section]) { + // Trigger sending packets even if they're empty. + pkt.updateFlag = UpdateFlag::PAST; + } + startRateLogic = TlmPacketizer_RateLogic::EVERY_MAX; + break; + case PACKET_UPDATE_AFTER_FIRST_CHANGE: + startRateLogic = TlmPacketizer_RateLogic::EVERY_MAX; + break; + default: + FW_ASSERT(0, PACKET_UPDATE_MODE); + break; + } + for (FwChanIdType group = 0; group < NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS; group++) { + Fw::Enabled groupEnabled = group <= startLevel ? Fw::Enabled::ENABLED : Fw::Enabled::DISABLED; + this->m_groupConfigs[section][group].set_enabled(groupEnabled); + this->m_groupConfigs[section][group].set_rateLogic(startRateLogic); + } + } // populate hash table with ignore list for (FwChanIdType channelEntry = 0; channelEntry < ignoreList.numEntries; channelEntry++) { @@ -328,47 +360,106 @@ Fw::TlmValid TlmPacketizer ::TlmGet_handler(FwIndexType portNum, //!< The port void TlmPacketizer ::Run_handler(const FwIndexType portNum, U32 context) { FW_ASSERT(this->m_configured); - // Only write packets if connected - if (not this->isConnected_PktSend_OutputPort(0)) { - return; - } - // lock mutex long enough to modify active telemetry buffer // so the data can be read without worrying about updates this->m_lock.lock(); // copy buffers from fill side to send side for (FwChanIdType pkt = 0; pkt < this->m_numPackets; pkt++) { - if ((this->m_fillBuffers[pkt].updated) and - ((this->m_fillBuffers[pkt].level <= this->m_startLevel) or (this->m_fillBuffers[pkt].requested))) { - this->m_sendBuffers[pkt] = this->m_fillBuffers[pkt]; - if (PACKET_UPDATE_ON_CHANGE == PACKET_UPDATE_MODE) { - this->m_fillBuffers[pkt].updated = false; - } - this->m_fillBuffers[pkt].requested = false; - // PACKET_UPDATE_AFTER_FIRST_CHANGE will be this case - updated flag will not be cleared - } else if ((PACKET_UPDATE_ALWAYS == PACKET_UPDATE_MODE) and - (this->m_fillBuffers[pkt].level <= this->m_startLevel)) { - this->m_sendBuffers[pkt] = this->m_fillBuffers[pkt]; - this->m_sendBuffers[pkt].updated = true; - } else { - this->m_sendBuffers[pkt].updated = false; - } + this->m_sendBuffers[pkt] = this->m_fillBuffers[pkt]; + this->m_fillBuffers[pkt].updated = false; } this->m_lock.unLock(); // push all updated packet buffers for (FwChanIdType pkt = 0; pkt < this->m_numPackets; pkt++) { - if (this->m_sendBuffers[pkt].updated) { - // serialize time into time offset in packet - Fw::ExternalSerializeBuffer buff( - &this->m_sendBuffers[pkt] - .buffer.getBuffAddr()[sizeof(FwPacketDescriptorType) + sizeof(FwTlmPacketizeIdType)], - Fw::Time::SERIALIZED_SIZE); - Fw::SerializeStatus stat = buff.serializeFrom(this->m_sendBuffers[pkt].latestTime); - FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat); - - this->PktSend_out(0, this->m_sendBuffers[pkt].buffer, 0); + FwChanIdType entryGroup = this->m_sendBuffers[pkt].level; + + // Iterate through output sections + for (FwIndexType section = 0; section < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS; section++) { + // Packet is updated and not REQUESTED (Prevent Downgrading) + if (this->m_sendBuffers[pkt].updated and + this->m_packetFlags[section][pkt].updateFlag != UpdateFlag::REQUESTED) + this->m_packetFlags[section][pkt].updateFlag = UpdateFlag::NEW; + + bool sendOutFlag = false; + + const FwIndexType outIndex = static_cast(section * NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS + + static_cast(entryGroup)); + PktSendCounters& pktEntryFlags = this->m_packetFlags[section][pkt]; + TlmPacketizer_GroupConfig& entryGroupConfig = this->m_groupConfigs[section][entryGroup]; + + /* Base conditions for sending + 1. Output port is connected + 2. The packet was requested (Override Checks). + + If the packet wasn't requested: + 3. The Section and Group in Section is enabled OR the Group in Section is force enabled + 4. The rate logic is not SILENCED. + 5. The packet has data (marked updated in the past or new) + */ + if (not this->isConnected_PktSend_OutputPort(outIndex)) + continue; + if (pktEntryFlags.updateFlag == UpdateFlag::REQUESTED) { + sendOutFlag = true; + } else { + if (not((entryGroupConfig.get_enabled() and this->m_sectionEnabled[section] == Fw::Enabled::ENABLED) or + entryGroupConfig.get_forceEnabled() == Fw::Enabled::ENABLED)) + continue; + if (entryGroupConfig.get_rateLogic() == Svc::TlmPacketizer_RateLogic::SILENCED) + continue; + if (pktEntryFlags.updateFlag == UpdateFlag::NEVER_UPDATED) + continue; // Avoid No Data + } + + // Update Counter, prevent overflow. + if (pktEntryFlags.prevSentCounter < std::numeric_limits::max()) + pktEntryFlags.prevSentCounter++; + + /* + 1. Packet has been updated + 2. Group Logic includes checking MIN + 3. Packet sent counter at MIN + */ + if (pktEntryFlags.updateFlag == UpdateFlag::NEW and + entryGroupConfig.get_rateLogic() != Svc::TlmPacketizer_RateLogic::EVERY_MAX and + pktEntryFlags.prevSentCounter >= entryGroupConfig.get_min()) + sendOutFlag = true; + + /* + 1. Group Logic includes checking MAX + 2. Packet set counter is at MAX + */ + if (entryGroupConfig.get_rateLogic() != Svc::TlmPacketizer_RateLogic::ON_CHANGE_MIN and + pktEntryFlags.prevSentCounter >= entryGroupConfig.get_max()) + sendOutFlag = true; + + // Send under the following conditions: + // 1. Packet received updates and it has been past delta min counts since last packet (min enabled) + // 2. Packet has passed delta max counts since last packet (max enabled) + // With the above, the group must be either enabled or force enabled. + // 3. If the packet was requested. + if (sendOutFlag) { + // serialize time into time offset in packet + Fw::ExternalSerializeBuffer buff( + &this->m_sendBuffers[pkt] + .buffer.getBuffAddr()[sizeof(FwPacketDescriptorType) + sizeof(FwTlmPacketizeIdType)], + Fw::Time::SERIALIZED_SIZE); + Fw::SerializeStatus stat = buff.serializeFrom(this->m_sendBuffers[pkt].latestTime); + FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat); + this->PktSend_out(outIndex, this->m_sendBuffers[pkt].buffer, pktEntryFlags.prevSentCounter); + pktEntryFlags.prevSentCounter = 0; + pktEntryFlags.updateFlag = UpdateFlag::PAST; + } } + this->m_sendBuffers[pkt].updated = false; + } +} + +void TlmPacketizer ::controlIn_handler(FwIndexType portNum, FwIndexType section, const Fw::Enabled& enabled) { + if (0 <= section && section < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS) { + this->m_sectionEnabled[section] = enabled; + } else { + this->log_WARNING_LO_SectionUnconfigurable(section, enabled); } } @@ -382,25 +473,34 @@ void TlmPacketizer ::pingIn_handler(const FwIndexType portNum, U32 key) { // ---------------------------------------------------------------------- void TlmPacketizer ::SET_LEVEL_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, FwChanIdType level) { - this->m_startLevel = level; - if (level > this->m_maxLevel) { - this->log_WARNING_LO_MaxLevelExceed(level, this->m_maxLevel); + if (level > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) { + this->log_WARNING_LO_MaxLevelExceed(level, MAX_CONFIGURABLE_TLMPACKETIZER_GROUP); + } + for (FwIndexType section = 0; section < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS; section++) { + for (FwChanIdType group = 0; group < NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS; group++) { + this->m_groupConfigs[section][group].set_enabled(group <= level ? Fw::Enabled::ENABLED + : Fw::Enabled::DISABLED); + } } - this->tlmWrite_SendLevel(level); + this->tlmWrite_GroupConfigs(this->m_groupConfigs); this->log_ACTIVITY_HI_LevelSet(level); this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); } -void TlmPacketizer ::SEND_PKT_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, U32 id) { +void TlmPacketizer ::SEND_PKT_cmdHandler(const FwOpcodeType opCode, + const U32 cmdSeq, + const U32 id, + const FwIndexType section) { FwChanIdType pkt = 0; for (pkt = 0; pkt < this->m_numPackets; pkt++) { if (this->m_fillBuffers[pkt].id == id) { this->m_lock.lock(); this->m_fillBuffers[pkt].updated = true; this->m_fillBuffers[pkt].latestTime = this->getTime(); - this->m_fillBuffers[pkt].requested = true; this->m_lock.unLock(); + this->m_packetFlags[section][pkt].updateFlag = UpdateFlag::REQUESTED; + this->log_ACTIVITY_LO_PacketSent(id); break; } @@ -416,6 +516,69 @@ void TlmPacketizer ::SEND_PKT_cmdHandler(const FwOpcodeType opCode, const U32 cm this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); } +void TlmPacketizer ::ENABLE_SECTION_cmdHandler(FwOpcodeType opCode, + U32 cmdSeq, + FwIndexType section, + Fw::Enabled enable) { + if (section < 0 or section >= NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS) { + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); + return; + } + this->m_sectionEnabled[section] = enable; + this->tlmWrite_SectionEnabled(this->m_sectionEnabled); + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +void TlmPacketizer ::ENABLE_GROUP_cmdHandler(FwOpcodeType opCode, + U32 cmdSeq, + FwIndexType section, + FwChanIdType tlmGroup, + Fw::Enabled enable) { + if ((0 <= section and section >= NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS) or + tlmGroup > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) { + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); + return; + } + this->m_groupConfigs[section][tlmGroup].set_enabled(enable); + this->tlmWrite_GroupConfigs(this->m_groupConfigs); + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +void TlmPacketizer ::FORCE_GROUP_cmdHandler(FwOpcodeType opCode, + U32 cmdSeq, + FwIndexType section, + FwChanIdType tlmGroup, + Fw::Enabled enable) { + if (section < 0 or section >= NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS or + tlmGroup > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) { + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); + return; + } + this->m_groupConfigs[section][tlmGroup].set_forceEnabled(enable); + this->tlmWrite_GroupConfigs(this->m_groupConfigs); + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +void TlmPacketizer ::SET_GROUP_DELTAS_cmdHandler(FwOpcodeType opCode, + U32 cmdSeq, + FwIndexType section, + FwChanIdType tlmGroup, + Svc::TlmPacketizer_RateLogic rateLogic, + U32 minDelta, + U32 maxDelta) { + if (section < 0 or section >= NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS or + tlmGroup > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) { + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); + return; + } + TlmPacketizer_GroupConfig& groupConfig = this->m_groupConfigs[section][tlmGroup]; + groupConfig.set_rateLogic(rateLogic); + groupConfig.set_min(minDelta); + groupConfig.set_max(maxDelta); + this->tlmWrite_GroupConfigs(this->m_groupConfigs); + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + FwChanIdType TlmPacketizer::doHash(FwChanIdType id) { return (id % TLMPACKETIZER_HASH_MOD_VALUE) % TLMPACKETIZER_NUM_TLM_HASH_SLOTS; } diff --git a/Svc/TlmPacketizer/TlmPacketizer.fpp b/Svc/TlmPacketizer/TlmPacketizer.fpp index f03dbafffa1..aeccd5a3f40 100644 --- a/Svc/TlmPacketizer/TlmPacketizer.fpp +++ b/Svc/TlmPacketizer/TlmPacketizer.fpp @@ -1,14 +1,47 @@ module Svc { - + port EnableSection ( + section: FwIndexType @< Section to enable (Primary, Secondary, etc...) + enabled: Fw.Enabled @< Enable / Disable Section + ) @ A component for storing telemetry active component TlmPacketizer { + # ---------------------------------------------------------------------- + # Types + # ---------------------------------------------------------------------- + + enum RateLogic { + SILENCED, + EVERY_MAX, + ON_CHANGE_MIN, + ON_CHANGE_MIN_OR_EVERY_MAX, + } + + struct GroupConfig { + enabled: Fw.Enabled @< Enable / Disable Telemetry Output + forceEnabled: Fw.Enabled @< Force Enable / Disable Telemetry Output + rateLogic: RateLogic @< Rate Logic Configuration + min: U32 @< Minimum Sched Ticks + max: U32 @< Maximum Sched Ticks + } default { + enabled = Fw.Enabled.ENABLED + forceEnabled = Fw.Enabled.DISABLED + rateLogic = RateLogic.ON_CHANGE_MIN + min = 0 + max = 0 + } + + array GroupConfigs = [NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS] GroupConfig + array SectionConfigs = [NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS] GroupConfigs + array SectionEnabled = [NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS] Fw.Enabled default Fw.Enabled.ENABLED # ---------------------------------------------------------------------- # General ports # ---------------------------------------------------------------------- @ Packet send port - output port PktSend: Fw.Com + output port PktSend: [NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS * NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS] Fw.Com + + async input port controlIn: EnableSection @ Ping input port async input port pingIn: Svc.Ping @@ -62,10 +95,46 @@ module Svc { @ Force a packet to be sent async command SEND_PKT( - $id: U32 @< The packet ID + $id: U32 @< The packet ID + section: FwIndexType @< Section to emit packet ) \ opcode 1 + @ Enable / disable telemetry of a group on a section + async command ENABLE_SECTION( + section: FwIndexType @< Section grouping to configure + enable: Fw.Enabled @< Section enabled or disabled + ) \ + opcode 2 + + @ Enable / disable telemetry of a group on a section + async command ENABLE_GROUP( + section: FwIndexType @< Section grouping to configure + tlmGroup: FwChanIdType @< Group Identifier + enable: Fw.Enabled @< Section enabled or disabled + ) \ + opcode 3 + + @ Force telemetering a group on a section, even if disabled + async command FORCE_GROUP( + section: FwIndexType @< Section grouping + tlmGroup: FwChanIdType @< Group Identifier + enable: Fw.Enabled @< Section enabled or disabled + ) \ + opcode 4 + + @ Set Min and Max Deltas between successive packets + async command SET_GROUP_DELTAS( + section: FwIndexType @< Section grouping + tlmGroup: FwChanIdType @< Group Identifier + rateLogic: RateLogic @< Rate Logic + minDelta: U32 @< Minimum Sched Ticks to send packets on updates + maxDelta: U32 @< Maximum Sched Ticks to send packets + ) \ + opcode 5 + + + # ---------------------------------------------------------------------- # Events # ---------------------------------------------------------------------- @@ -111,12 +180,21 @@ module Svc { id 4 \ format "Could not find packet ID {}" + event SectionUnconfigurable( + section: FwIndexType @< The Section + enable: Fw.Enabled @< Attempted Configuration + ) \ + severity warning low \ + id 5 \ + format "Section {} is unconfigurable and cannot be set to {}" + # ---------------------------------------------------------------------- # Telemetry # ---------------------------------------------------------------------- @ Telemetry send level - telemetry SendLevel: FwChanIdType id 0 + telemetry GroupConfigs: SectionConfigs id 0 + telemetry SectionEnabled: SectionEnabled id 1 } diff --git a/Svc/TlmPacketizer/TlmPacketizer.hpp b/Svc/TlmPacketizer/TlmPacketizer.hpp index 08948d50558..dafddbcab7b 100644 --- a/Svc/TlmPacketizer/TlmPacketizer.hpp +++ b/Svc/TlmPacketizer/TlmPacketizer.hpp @@ -11,6 +11,7 @@ #ifndef TlmPacketizer_HPP #define TlmPacketizer_HPP +#include "Fw/Types/EnabledEnumAc.hpp" #include "Os/Mutex.hpp" #include "Svc/TlmPacketizer/TlmPacketizerComponentAc.hpp" #include "Svc/TlmPacketizer/TlmPacketizerTypes.hpp" @@ -19,6 +20,8 @@ namespace Svc { class TlmPacketizer final : public TlmPacketizerComponentBase { + friend class TlmPacketizerTester; + public: // ---------------------------------------------------------------------- // Construction, initialization, and destruction @@ -57,6 +60,12 @@ class TlmPacketizer final : public TlmPacketizerComponentBase { U32 context /*!< The call order*/ ) override; + //! Handler implementation for controlIn + void controlIn_handler(FwIndexType portNum, //!< The port number + FwIndexType section, //!< Section to enable (Primary, Secondary, etc...) + const Fw::Enabled& enabled //!< Enable / Disable Section + ) override; + //! Handler implementation for pingIn //! void pingIn_handler(const FwIndexType portNum, /*!< The port number*/ @@ -82,9 +91,44 @@ class TlmPacketizer final : public TlmPacketizerComponentBase { //! Force a packet to be sent void SEND_PKT_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/ const U32 cmdSeq, /*!< The command sequence number*/ - U32 id /*!< The packet ID*/ + const U32 id, /*!< The packet ID*/ + const FwIndexType section /*!< Section to emit packet*/ ) override; + //! Handler implementation for command ENABLE_SECTION + void ENABLE_SECTION_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + FwIndexType section, //!< Section to configure + Fw::Enabled enable //!< Active Sending Group + ) override; + + //! Handler implementation for command ENABLE_GROUP + //! + //! Enable / disable telemetry of a group on a section + void ENABLE_GROUP_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + FwIndexType section, //!< section grouping to configure + FwChanIdType tlmGroup, //!< Group Level + Fw::Enabled enable //!< Active Sending Group + ) override; + + //! Handler implementation for command FORCE_GROUP + void FORCE_GROUP_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + FwIndexType section, //!< Section to configure + FwChanIdType tlmGroup, //!< Group Level + Fw::Enabled enable //!< Active Sending Group + ) override; + + //! Handler implementation for command SET_GROUP_DELTAS + void SET_GROUP_DELTAS_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + FwIndexType section, //!< Section to configure + FwChanIdType tlmGroup, //!< Group Level + Svc::TlmPacketizer_RateLogic rateLogic, //!< Rate Logic + U32 minDelta, + U32 maxDelta) override; + // number of packets to fill FwChanIdType m_numPackets; // Array of packet buffers to send @@ -96,7 +140,6 @@ class TlmPacketizer final : public TlmPacketizerComponentBase { FwChanIdType id; //!< channel id FwChanIdType level; //!< channel level bool updated; //!< if packet had any updates during last cycle - bool requested; //!< if the packet was requested with SEND_PKT in the last cycle }; // buffers for filling with telemetry @@ -139,8 +182,21 @@ class TlmPacketizer final : public TlmPacketizerComponentBase { TlmEntry* findBucket(FwChanIdType id); - FwChanIdType m_startLevel; //!< initial level for sending packets - FwChanIdType m_maxLevel; //!< maximum level in all packets + TlmPacketizer_SectionEnabled m_sectionEnabled{}; + + TlmPacketizer_SectionConfigs m_groupConfigs{}; + + enum UpdateFlag : U8 { + NEVER_UPDATED = 0, + PAST = 1, + NEW = 2, + REQUESTED = 3, + }; + + struct PktSendCounters { + U32 prevSentCounter = std::numeric_limits::max(); // Prevent Start up spam + UpdateFlag updateFlag = UpdateFlag::NEVER_UPDATED; + } m_packetFlags[NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS][MAX_PACKETIZER_PACKETS]{}; }; } // end namespace Svc diff --git a/Svc/TlmPacketizer/docs/img/TlmPacketizerBDD.jpg b/Svc/TlmPacketizer/docs/img/TlmPacketizerBDD.jpg index e29e86afc21..f3a1ef7650c 100644 Binary files a/Svc/TlmPacketizer/docs/img/TlmPacketizerBDD.jpg and b/Svc/TlmPacketizer/docs/img/TlmPacketizerBDD.jpg differ diff --git a/Svc/TlmPacketizer/docs/sdd.md b/Svc/TlmPacketizer/docs/sdd.md index 894c3d21a63..23d48ac2880 100644 --- a/Svc/TlmPacketizer/docs/sdd.md +++ b/Svc/TlmPacketizer/docs/sdd.md @@ -4,6 +4,8 @@ The `Svc::TlmPacketizer` Component is used to store telemetry values written by other components. The values are stored in serialized form. TlmPacketizer differs from `Svc::TlmChan` in that it stores telemetry in defined packets instead of streaming the updates as they come. The defined packets are passed in as a table to the `setPacketList()` public method. When telemetry updates are passed to the component, they are placed at the offset in a packet buffer defined by the table. When the `run()` port is called, all the defined packets are sent to the output port with the most recent . This is meant to replace `Svc::TlmCham` for use cases where a more compact packet format is desired. The disadvantage is that all channels are pushed whether or not they have been updated. +Uses can change the individual rates at which groups per group instance are outputted upon a `run()` sched tick. Each group on each output section has independently configurable telemetry resampling rates. Packets can be sent on change with a rate limiting enforced minimum number of ticks between updates. Or packets can be sent with at a guaranteed rate of a maximum number of ticks between updates. Packets are evaluated individually and have a counter since last invocation. MIN and MAX logic are selectable, for users that desire only "on change" telemetry, MAX invocations between telemetry points, both, or none at all. + ## 2. Requirements The requirements for `Svc::TlmPacketizer` are as follows: @@ -29,13 +31,21 @@ The `Svc::TlmPacketizer` component has the following component diagram: #### 3.1.2 Ports -The `Svc::TlmChan` component uses the following port types: +The `Svc::TlmPacketizer` component uses the following port types: Port Data Type | Name | Direction | Kind | Usage -------------- | ---- | --------- | ---- | ----- [`Svc::Sched`](../../Sched/docs/sdd.md) | Run | Input | Asynchronous | Execute a cycle to write changed telemetry channels [`Fw::Tlm`](../../../Fw/Tlm/docs/sdd.md) | TlmRecv | Input | Synchronous Input | Update a telemetry channel [`Fw::Com`](../../../Fw/Com/docs/sdd.md) | PktSend | Output | n/a | Write a set of packets with updated telemetry +`Svc::EnableSection` | controlIn | Input | Asynchronous | Enable / Disable sections of telemetry groups + +#### 3.1.3 Terminology +Telemetry Point: An emitted value. +Telemetry Channel: A tagged type and identifier for emitting telemetry points. +Telemetry Packets: A group of telemetry channels. +Telemetry Group / Level: An identifier for a telemetry packet used to determine which packets get transmitted. +Telemetry Section: A resampling of telemetry groups. Each Section typically is destined for a different destination (e.g. realtime downlink vs store & forward) #### 3.2 Functional Description @@ -43,12 +53,38 @@ The `Svc::TlmPacketizer` component has an input port `TlmRecv` that receives cha The implementation uses a hashing function to find the location of telemetry channels that is tuned in the configuration file `TlmPacketizerImplCfg.hpp`. See section 3.5 for description. -When a call to the `Run()` interface is called, the packet writes are locked and all the packets are copied to a second set of packets. Once the copy is complete, the packets writes are unlocked. The destination packet set gets updated with the current time tag and are sent out the `pktSend()` port. +When a call to the `Run()` interface is called, the packet writes are locked and all the packets are copied to a second set of packets. Once the copy is complete, the packets writes are unlocked. The destination packet set gets updated with the current time tag and are sent out the `pktSend` port. + +Each telemetry group, depending on section and group configurations, are sent out on several indices of the `pktSend` port. Since each section holds a continuous range of ports, a packet with group 1 with a TlmPacketizer configuration of 3 sections (0, 1, and 2), and a max group of 3 (0 and 3 inclusive), will be sent on 3 indices: 1, 5, and 9. Port invocations to `controlIn` or the command, `ENABLE_SECTION` are used to enable / disable each section, supporting downstream components that rely on different samplings of groups of telemetry. Each group instance is separately sampled from each other, allowing for individual rates per section and group. ### 3.3 Scenarios #### 3.3.1 External User Option +#### 3.3.2 Configuring Telemetry Group Rates Per Port + +The `Svc::TlmPacketizer` is configurable to have multiple `PktSend` group outputs using the fpp constant, `MAX_CONFIGURABLE_TLMPACKETIZER_GROUP`. Doing so allows each packet group have different configurations for each `PktSend` output section. + +`PktSend` output is ordered by `[section][group]`, where within each section telemetry groups are emitted. Each group within each section are separately sampled, and have their own configuration rates and logic independent from each other. + +Each group is configured with min/max delta parameters, as well as a logic gate determining its output rate behavior. Deltas are counters of sched ticks invoked through the `run()` port. Min Delta is the least number of `Run()` invocations between successive packets before a packet with an updated channel is allowed to be sent. Updated packets will not be sent until their counter reaches Min Delta. This mitigates against telemetry spam while allowing users to benefit from asynchronously updating channels (e.g. those that don't occupy a set schedule). Max Delta is the maximum number of `Run()` invocations between successive packets. Upon reaching this counter, the packet would be sent, regardless of change. + +Each telemetry group per section is configured with a `RateLogic` parameter: +* `SILENCED`: Packet will never be sent +* `EVERY_MAX`: Packet will be sent every Max Delta intervals +* `ON_CHANGE_MIN`: Packet will only be sent on updates at at least Min Delta intervals. +* `ON_CHANGE_MIN_OR_EVERY_MAX`: Packet will be sent on updates at at least Min Delta intervals, or at most Max Delta intervals. + +Groups are configured using the `SET_GROUP_DELTAS` command. + +#### 3.3.3 Switching Telemetry Sections +`Svc::TlmPacketizer` also has a configurable constant `NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS` that allows separately streamed outputs of packets of the same group. This is useful for downstream components that would handle different telemetry groups at different rates. Examples of this configuration includes live telemetry throughput, detailed sim reconstruction, or critical data operations. + +Telemetry sections are enabled and disabled upon spacecraft state transitions through `controlIn()` port invocations or via operator commands. A section and group pair must be both enabled to emit a telemetry packet. +* `ENABLE_SECTION` / `controlIn()`: Enable / Disable telemetry sections. +* `ENABLE_GROUP`: Within a section, Enable / Disable a telemetry group. +* `FORCE_GROUP`: If set to Enabled, telemetry of the chosen group in a section will be emitted regardless if the section or group within the section is disabled. + ### 3.4 State `Svc::TlmPacketizer` has no state machines. @@ -71,4 +107,5 @@ To see unit test coverage run fprime-util check --coverage Date | Description ---- | ----------- 12/14/2017 | Initial version +01/23/2026 | Added group level rate logic diff --git a/Svc/TlmPacketizer/test/ut/TlmPacketizerTester.cpp b/Svc/TlmPacketizer/test/ut/TlmPacketizerTester.cpp index 6e387af7d50..2a85d7c28f7 100644 --- a/Svc/TlmPacketizer/test/ut/TlmPacketizerTester.cpp +++ b/Svc/TlmPacketizer/test/ut/TlmPacketizerTester.cpp @@ -42,11 +42,20 @@ TlmPacketizerChannelEntry packet1List[] = {{10, 4}, {100, 2}, {333, 1}}; TlmPacketizerChannelEntry packet2List[] = {{10, 4}, {13, 8}, {250, 2}, {22, 1}}; +TlmPacketizerChannelEntry packet3List[] = {{67, 4}}; + +TlmPacketizerChannelEntry packet4List[] = {{10, 4}, {60, 4}}; + TlmPacketizerPacket packet1 = {packet1List, 4, 1, FW_NUM_ARRAY_ELEMENTS(packet1List)}; TlmPacketizerPacket packet2 = {packet2List, 8, 2, FW_NUM_ARRAY_ELEMENTS(packet2List)}; +TlmPacketizerPacket packet3 = {packet3List, 12, 2, FW_NUM_ARRAY_ELEMENTS(packet3List)}; + +TlmPacketizerPacket packet4 = {packet4List, 16, 3, FW_NUM_ARRAY_ELEMENTS(packet4List)}; + TlmPacketizerPacketList packetList = {{&packet1, &packet2}, 2}; +TlmPacketizerPacketList packetList2 = {{&packet1, &packet2, &packet3, &packet4}, 4}; TlmPacketizerChannelEntry ignoreList[] = {{25, 0}, {50, 0}}; @@ -143,7 +152,8 @@ void TlmPacketizerTester ::sendPacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(14))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // No recently sent packet 1. Context set to sent counter, so this will be at max value. + ASSERT_from_PktSend(0, comBuff, static_cast(std::numeric_limits::max())); comBuff.resetSer(); ASSERT_EQ(Fw::FW_SERIALIZE_OK, @@ -155,7 +165,8 @@ void TlmPacketizerTester ::sendPacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1010))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); - ASSERT_from_PktSend(1, comBuff, static_cast(0)); + // No recently sent packet 2. Context set to sent counter, so this will be at max value. + ASSERT_from_PktSend(1, comBuff, static_cast(std::numeric_limits::max())); } void TlmPacketizerTester ::sendPacketLevelsTest() { @@ -211,7 +222,8 @@ void TlmPacketizerTester ::sendPacketLevelsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(14))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // No recently sent packet 1. Context set to sent counter, so this will be at max value. + ASSERT_from_PktSend(0, comBuff, static_cast(std::numeric_limits::max())); comBuff.resetSer(); ASSERT_EQ(Fw::FW_SERIALIZE_OK, @@ -223,7 +235,8 @@ void TlmPacketizerTester ::sendPacketLevelsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1010))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); - ASSERT_from_PktSend(1, comBuff, static_cast(0)); + // No recently sent packet 2. Context set to sent counter, so this will be at max value. + ASSERT_from_PktSend(1, comBuff, static_cast(std::numeric_limits::max())); } void TlmPacketizerTester ::updatePacketsTest() { @@ -261,7 +274,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // No recently sent packet 1. Context set to sent counter, so this will be at max value. + ASSERT_from_PktSend(0, comBuff, static_cast(std::numeric_limits::max())); comBuff.resetSer(); ASSERT_EQ(Fw::FW_SERIALIZE_OK, @@ -273,7 +287,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); - ASSERT_from_PktSend(1, comBuff, static_cast(0)); + // No recently sent packet 1. Context set to sent counter, so this will be at max value. + ASSERT_from_PktSend(1, comBuff, static_cast(std::numeric_limits::max())); // second channel @@ -300,7 +315,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // Packet 1 sent recently with a delta sched ticks of 1 + ASSERT_from_PktSend(0, comBuff, static_cast(1)); buff.resetSer(); ts.add(1, 0); @@ -322,7 +338,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(14))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // Packet 1 sent recently with a delta sched ticks of 1 + ASSERT_from_PktSend(0, comBuff, static_cast(1)); buff.resetSer(); ts.add(1, 0); @@ -344,7 +361,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // Packet 2 sent recently with a delta sched ticks of 3 + ASSERT_from_PktSend(0, comBuff, static_cast(3)); buff.resetSer(); ts.add(1, 0); @@ -368,7 +386,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1010))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // Packet 2 sent recently with a delta sched ticks of 1 + ASSERT_from_PktSend(0, comBuff, static_cast(1)); buff.resetSer(); ts.add(1, 0); @@ -390,7 +409,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1010))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // Packet 2 sent recently with a delta sched ticks of 1 + ASSERT_from_PktSend(0, comBuff, static_cast(1)); //** Update all the packets again with new values @@ -414,7 +434,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(14))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // Packet 1 sent recently with a delta sched ticks of 4 + ASSERT_from_PktSend(0, comBuff, static_cast(4)); comBuff.resetSer(); ASSERT_EQ(Fw::FW_SERIALIZE_OK, @@ -426,7 +447,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1010))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); - ASSERT_from_PktSend(1, comBuff, static_cast(0)); + // Packet 2 sent recently with a delta sched ticks of 1 + ASSERT_from_PktSend(1, comBuff, static_cast(1)); // second channel @@ -449,7 +471,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(550))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(14))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // Packet 1 sent recently with a delta sched ticks of 1 + ASSERT_from_PktSend(0, comBuff, static_cast(1)); buff.resetSer(); ts.add(1, 0); @@ -470,7 +493,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(550))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(211))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // Packet 1 sent recently with a delta sched ticks of 1 + ASSERT_from_PktSend(0, comBuff, static_cast(1)); buff.resetSer(); ts.add(1, 0); @@ -492,7 +516,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1010))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // Packet 2 sent recently with a delta sched ticks of 3 + ASSERT_from_PktSend(0, comBuff, static_cast(3)); buff.resetSer(); ts.add(1, 0); @@ -514,7 +539,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(8649))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // Packet 2 sent recently with a delta sched ticks of 1 + ASSERT_from_PktSend(0, comBuff, static_cast(1)); buff.resetSer(); ts.add(1, 0); @@ -536,7 +562,8 @@ void TlmPacketizerTester ::updatePacketsTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(8649))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(65))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // Packet 2 sent recently with a delta sched ticks of 1 + ASSERT_from_PktSend(0, comBuff, static_cast(1)); } void TlmPacketizerTester ::ignoreTest() { @@ -574,7 +601,8 @@ void TlmPacketizerTester ::ignoreTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); - ASSERT_from_PktSend(0, comBuff, static_cast(0)); + // First Packet 1 Send. Delta Sched Ticks = Max + ASSERT_from_PktSend(0, comBuff, static_cast(std::numeric_limits::max())); comBuff.resetSer(); ASSERT_EQ(Fw::FW_SERIALIZE_OK, @@ -586,7 +614,8 @@ void TlmPacketizerTester ::ignoreTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(0))); - ASSERT_from_PktSend(1, comBuff, static_cast(0)); + // First Packet 2 Send. Delta Sched Ticks = Max + ASSERT_from_PktSend(1, comBuff, static_cast(std::numeric_limits::max())); // ignored channel @@ -658,7 +687,8 @@ void TlmPacketizerTester ::sendManualPacketTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff1.serializeFrom(static_cast(15))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff1.serializeFrom(static_cast(14))); - ASSERT_from_PktSend(0, comBuff1, static_cast(0)); + // First Packet 1 Send. Delta Sched Ticks = Max + ASSERT_from_PktSend(0, comBuff1, static_cast(std::numeric_limits::max())); Fw::ComBuffer comBuff2; ASSERT_EQ(Fw::FW_SERIALIZE_OK, @@ -670,7 +700,8 @@ void TlmPacketizerTester ::sendManualPacketTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff2.serializeFrom(static_cast(1010))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff2.serializeFrom(static_cast(15))); - ASSERT_from_PktSend(1, comBuff2, static_cast(0)); + // First Packet 2 Send. Delta Sched Ticks = Max + ASSERT_from_PktSend(1, comBuff2, static_cast(std::numeric_limits::max())); // should not be any new packets this->clearHistory(); @@ -681,7 +712,7 @@ void TlmPacketizerTester ::sendManualPacketTest() { ASSERT_from_PktSend_SIZE(0); // send command to manually send a packet - this->sendCmd_SEND_PKT(0, 12, 4); + this->sendCmd_SEND_PKT(0, 12, 4, 0); this->component.doDispatch(); ASSERT_EVENTS_SIZE(1); ASSERT_EVENTS_PacketSent(0, 4); @@ -691,7 +722,8 @@ void TlmPacketizerTester ::sendManualPacketTest() { this->invoke_to_Run(0, 0); this->component.doDispatch(); ASSERT_from_PktSend_SIZE(1); - ASSERT_from_PktSend(0, comBuff1, static_cast(0)); + // Packet 1 Sent recently. Delta Sched Ticks = 2 + ASSERT_from_PktSend(0, comBuff1, static_cast(2)); // another packet this->clearHistory(); @@ -703,7 +735,7 @@ void TlmPacketizerTester ::sendManualPacketTest() { // send command to manually send a packet this->clearHistory(); - this->sendCmd_SEND_PKT(0, 12, 8); + this->sendCmd_SEND_PKT(0, 12, 8, 0); this->component.doDispatch(); ASSERT_EVENTS_SIZE(1); ASSERT_EVENTS_PacketSent(0, 8); @@ -713,12 +745,13 @@ void TlmPacketizerTester ::sendManualPacketTest() { this->invoke_to_Run(0, 0); this->component.doDispatch(); ASSERT_from_PktSend_SIZE(1); - ASSERT_from_PktSend(0, comBuff2, static_cast(0)); + // Packet 2 Sent recently. Delta Sched Ticks = 4 + ASSERT_from_PktSend(0, comBuff2, static_cast(4)); // Try to send invalid packet // send command to manually send a packet this->clearHistory(); - this->sendCmd_SEND_PKT(0, 12, 20); + this->sendCmd_SEND_PKT(0, 12, 20, 0); this->component.doDispatch(); ASSERT_EVENTS_SIZE(1); ASSERT_EVENTS_PacketNotFound(0, 20); @@ -777,8 +810,7 @@ void TlmPacketizerTester ::setPacketLevelTest() { ASSERT_EVENTS_LevelSet_SIZE(1); ASSERT_EVENTS_LevelSet(0, 1); ASSERT_TLM_SIZE(1); - ASSERT_TLM_SendLevel_SIZE(1); - ASSERT_TLM_SendLevel(0, 1); + ASSERT_TLM_GroupConfigs_SIZE(1); // send the packets // first channel @@ -830,37 +862,10 @@ void TlmPacketizerTester ::setPacketLevelTest() { ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff1.serializeFrom(static_cast(0x15))); ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff1.serializeFrom(static_cast(0x14))); - ASSERT_from_PktSend(0, comBuff1, static_cast(0)); + // First Packet 1 Send. Delta Sched Ticks = Max + ASSERT_from_PktSend(0, comBuff1, static_cast(std::numeric_limits::max())); return; - - ASSERT_FROM_PORT_HISTORY_SIZE(2); - ASSERT_from_PktSend_SIZE(2); - - // construct the packet buffers and make sure they are correct - - Fw::ComBuffer comBuff; - ASSERT_EQ(Fw::FW_SERIALIZE_OK, - comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(4))); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(20))); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(14))); - - ASSERT_from_PktSend(0, comBuff, static_cast(0)); - - comBuff.resetSer(); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, - comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(8))); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(20))); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1000000))); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1010))); - ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(15))); - - ASSERT_from_PktSend(1, comBuff, static_cast(0)); } void TlmPacketizerTester ::nonPacketizedChannelTest() { @@ -940,11 +945,575 @@ void TlmPacketizerTester ::getChannelValueTest() { ASSERT_EQ(valid, Fw::TlmValid::INVALID); } +//! Configured tlm groups test +//! +void TlmPacketizerTester ::configuredTelemetryGroupsTests(void) { + this->component.setPacketList(packetList2, ignore, 4); + this->m_primaryTestLock = false; + Fw::Time time; + Fw::TlmBuffer buffer; + + // Set level to high to enable all levels + this->sendCmd_SET_LEVEL(0, 0, 10); + this->component.doDispatch(); + + // Group 1 + this->sendCmd_SET_GROUP_DELTAS(0, 0, 0, 1, Svc::TlmPacketizer_RateLogic::ON_CHANGE_MIN, 3, 3); + this->component.doDispatch(); + + // Send every 5 on port 1 + this->sendCmd_SET_GROUP_DELTAS(0, 0, 1, 1, Svc::TlmPacketizer_RateLogic::ON_CHANGE_MIN, 2, 2); + this->component.doDispatch(); + + this->clearHistory(); + + // Group 2 + this->sendCmd_SET_GROUP_DELTAS(0, 0, 0, 2, Svc::TlmPacketizer_RateLogic::ON_CHANGE_MIN_OR_EVERY_MAX, 4, 12); + this->component.doDispatch(); + + this->sendCmd_SET_GROUP_DELTAS(0, 0, 1, 2, Svc::TlmPacketizer_RateLogic::SILENCED, 0, 0); + this->component.doDispatch(); + + this->clearHistory(); + + // Group 3 + this->sendCmd_SET_GROUP_DELTAS(0, 0, 1, 3, Svc::TlmPacketizer_RateLogic::ON_CHANGE_MIN_OR_EVERY_MAX, 0, 7); + this->component.doDispatch(); + + this->sendCmd_SET_GROUP_DELTAS(0, 0, 0, 3, Svc::TlmPacketizer_RateLogic::EVERY_MAX, 0, 6); + this->component.doDispatch(); + + // Disable output on section 2 via port invocation + this->invoke_to_controlIn(0, 2, Fw::Enabled::DISABLED); + this->component.doDispatch(); + + this->clearHistory(); + + /* + Configuration: + Section 0 Group 1: 3, 15 MIN 3 + Section 1 Group 1: 2, 14 MIN 2 + Section 0 Group 2: 1, 4, 13, 16. MIN 4, MAX 12 + Section 1 Group 3: 0, 7, 12, 18. MIN 0, MAX 7 + Section 0 Group 3: 6, 18. MAX 6 + Section 1 group 2 Ignored + */ + + /* + clang-format off + + T=0 Tests Updates of packets 1,2, and 4 for Groups 1,2, and 4. Updated Packets are emitted. + T=1 Tests Updates of packets 1,2, and 3. + Packet 3 is emitted, while Packet 2 is not due to < MIN (Each packet has their own counter) + T=2 Packet 1 emits after passing MIN (configured for port 1, group 1, updated at T=1) + T=3 Packet 1 emits after passing MIN (configured for port 0, group 1, updated at T=1) + T=4 Packet 2 emits after passing MIN (Received update at T=1) + T=4 Test updates packet 2 for group 2. + This tests updating a packet when time = MIN, and should be emitted. (Packet 2 and 3 have their own counters) + T=6 Packet 4 emits on port 1 after passing MAX (configured for port 1, group 3). + T=7 Packet 4 emits on port 0 after passing MAX, even if it had received no updates. + + T=12 Tests updating packets 1, 2, and 4. + Packet 4 on is emitted since it is updated after MIN and before MAX. + Packets 1 and 2 are updated after MIN and may also be at MAX, which is then emitted. + + + Packet Updates 1,2,4 1,2,3 1,2,4 + V V V + T=0 T=1 T=2 T=3 T=4 T=5 T=6 T=7 T=8 T=9 T=10 T=11 T=12 + + (Bass Clef) -|-------------------------------|-------------------------------|-------------------------------|- + Section 0 Group 1 ● ● | | ● + Section 1 Group 1 -●---------------●---------------|-------------------------------|-------------------------------●- + Section 0 Group 2 ● ● ● | ● + Section 1 Group 3 -●-------------------------------|-----------------------●-------|-------------------------------●- + Section 0 Group 3 ● | ● | ● + Section 1 Group 2 -|-------------------------------|-------------------------------|-------------------------------|- + | | | | + -|-------------------------------|-------------------------------|-------------------------------|- + | + Note: Packets 2 and 3 are updated and have their own independent counters! + + Expected Output: 5 1 1 1 1 0 1 1 0 0 0 0 5 + + clang-format on + */ + + // 1st Channel (Pkt 1, 2, 4) + buffer.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(1))); + this->invoke_to_TlmRecv(0, 10, time, buffer); + + // 2nd Channel (Pkt 1) + buffer.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(2))); + this->invoke_to_TlmRecv(0, 100, time, buffer); + + // 3rd Channel (Pkt 1) + buffer.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(3))); + this->invoke_to_TlmRecv(0, 333, time, buffer); + + // 2nd Channel (Pkt 2) + buffer.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(2))); + this->invoke_to_TlmRecv(0, 13, time, buffer); + + // 3rd Channel (Pkt 2) + buffer.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(3))); + this->invoke_to_TlmRecv(0, 250, time, buffer); + + // 4th Channel (Pkt 2) + buffer.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(4))); + this->invoke_to_TlmRecv(0, 22, time, buffer); + + // 2nd Channel (Pkt 4) + buffer.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(2))); + this->invoke_to_TlmRecv(0, 60, time, buffer); + + // run scheduler port to send packets + // T = 0 + this->invoke_to_Run(0, 0); + + this->component.doDispatch(); + + ASSERT_FROM_PORT_HISTORY_SIZE(5); + ASSERT_from_PktSend_SIZE(5); + + // Packet Location Indices (Checking proper Section, Group) + ASSERT_EQ(this->m_portOutInvokes[0][1], 1); + ASSERT_EQ(this->m_portOutInvokes[1][1], 1); + ASSERT_EQ(this->m_portOutInvokes[0][2], 1); + ASSERT_EQ(this->m_portOutInvokes[1][3], 1); + ASSERT_EQ(this->m_portOutInvokes[0][3], 1); + ASSERT_EQ(this->m_portOutInvokes[1][2], 0); + + // construct the packet buffers and make sure they are correct + + // Pkt 1 + Fw::ComBuffer comBuff; + ASSERT_EQ(Fw::FW_SERIALIZE_OK, + comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(4))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(2))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(3))); + + // First Packet 1 Send. Delta Sched Ticks = Max + ASSERT_from_PktSend(0, comBuff, static_cast(std::numeric_limits::max())); + ASSERT_from_PktSend(1, comBuff, static_cast(std::numeric_limits::max())); + + // Pkt 2 + comBuff.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, + comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(8))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(2))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(3))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(4))); + + // First Packet 2 Send. Delta Sched Ticks = Max + ASSERT_from_PktSend(2, comBuff, static_cast(std::numeric_limits::max())); + + // Pkt 4 + comBuff.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, + comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(16))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(2))); + + // First Packet 4 Send. Delta Sched Ticks = Max + ASSERT_from_PktSend(3, comBuff, static_cast(std::numeric_limits::max())); + ASSERT_from_PktSend(4, comBuff, static_cast(std::numeric_limits::max())); + + this->clearHistory(); + + // 2nd Channel (Pkt 1) + buffer.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(22))); + this->invoke_to_TlmRecv(0, 100, time, buffer); + + // 2nd Channel (Pkt 2) + buffer.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(22))); + this->invoke_to_TlmRecv(0, 13, time, buffer); + + // 1st Channel (Pkt 3) + buffer.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(11))); + this->invoke_to_TlmRecv(0, 67, time, buffer); + + // T = 1 + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + + ASSERT_FROM_PORT_HISTORY_SIZE(1); + ASSERT_from_PktSend_SIZE(1); + + // Packet Location Indices (Checking proper Section, Group) + ASSERT_EQ(this->m_portOutInvokes[0][1], 1); + ASSERT_EQ(this->m_portOutInvokes[1][1], 1); + ASSERT_EQ(this->m_portOutInvokes[0][2], 2); + ASSERT_EQ(this->m_portOutInvokes[1][3], 1); + ASSERT_EQ(this->m_portOutInvokes[0][3], 1); + ASSERT_EQ(this->m_portOutInvokes[1][2], 0); + + // Pkt 3 + comBuff.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, + comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(12))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(11))); + + // First Packet 3 Send. Delta Sched Ticks = Max + ASSERT_from_PktSend(0, comBuff, static_cast(std::numeric_limits::max())); + + this->clearHistory(); + + // T = 2 + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + + ASSERT_FROM_PORT_HISTORY_SIZE(1); + ASSERT_from_PktSend_SIZE(1); + + // Packet Location Indices (Checking proper Section, Group) + ASSERT_EQ(this->m_portOutInvokes[0][1], 1); + ASSERT_EQ(this->m_portOutInvokes[1][1], 2); + ASSERT_EQ(this->m_portOutInvokes[0][2], 2); + ASSERT_EQ(this->m_portOutInvokes[1][3], 1); + ASSERT_EQ(this->m_portOutInvokes[0][3], 1); + ASSERT_EQ(this->m_portOutInvokes[1][2], 0); + + // Pkt 1 + comBuff.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, + comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(4))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(22))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(3))); + + // Pkt 1 on section 1 + // Pkt 1 on section 1 sent recently with a delta of 2 + ASSERT_from_PktSend(0, comBuff, static_cast(2)); + + this->clearHistory(); + + // T = 3 + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + + ASSERT_FROM_PORT_HISTORY_SIZE(1); + ASSERT_from_PktSend_SIZE(1); + + // Packet Location Indices (Checking proper Section, Group) + ASSERT_EQ(this->m_portOutInvokes[0][1], 2); + ASSERT_EQ(this->m_portOutInvokes[1][1], 2); + ASSERT_EQ(this->m_portOutInvokes[0][2], 2); + ASSERT_EQ(this->m_portOutInvokes[1][3], 1); + ASSERT_EQ(this->m_portOutInvokes[0][3], 1); + ASSERT_EQ(this->m_portOutInvokes[1][2], 0); + + // Pkt 1 on section 0 + // comBuff unchanged since this->m_testTime is the same + // Pkt 1 on section 0 sent recently with a delta of 3 + ASSERT_from_PktSend(0, comBuff, static_cast(3)); + + this->clearHistory(); + + // T = 4 + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + + ASSERT_FROM_PORT_HISTORY_SIZE(1); + ASSERT_from_PktSend_SIZE(1); + + // Packet Location Indices (Checking proper Section, Group) + ASSERT_EQ(this->m_portOutInvokes[0][1], 2); + ASSERT_EQ(this->m_portOutInvokes[1][1], 2); + ASSERT_EQ(this->m_portOutInvokes[0][2], 3); + ASSERT_EQ(this->m_portOutInvokes[1][3], 1); + ASSERT_EQ(this->m_portOutInvokes[0][3], 1); + ASSERT_EQ(this->m_portOutInvokes[1][2], 0); + + // Pkt 2 on Port 0 + comBuff.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, + comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(8))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(22))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(3))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(4))); + + // Pkt 2 on section 0 sent recently with a delta of 4 + ASSERT_from_PktSend(0, comBuff, static_cast(4)); + + this->clearHistory(); + + // T = 5 + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + + // Not expecting any packets + ASSERT_FROM_PORT_HISTORY_SIZE(0); + ASSERT_from_PktSend_SIZE(0); + + // T = 6 + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + + ASSERT_FROM_PORT_HISTORY_SIZE(1); + ASSERT_from_PktSend_SIZE(1); + + // Packet Location Indices (Checking proper Section, Group) + ASSERT_EQ(this->m_portOutInvokes[0][1], 2); + ASSERT_EQ(this->m_portOutInvokes[1][1], 2); + ASSERT_EQ(this->m_portOutInvokes[0][2], 3); + ASSERT_EQ(this->m_portOutInvokes[1][3], 1); + ASSERT_EQ(this->m_portOutInvokes[0][3], 2); + ASSERT_EQ(this->m_portOutInvokes[1][2], 0); + + // Pkt 4 on section 1 (Unchanged since T = 0) + comBuff.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, + comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(16))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(1))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(2))); + + // Pkt 4 on section 0 sent recently with a delta of 6 + ASSERT_from_PktSend(0, comBuff, static_cast(6)); + + this->clearHistory(); + + // T = 7 + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + + ASSERT_FROM_PORT_HISTORY_SIZE(1); + ASSERT_from_PktSend_SIZE(1); + + // Packet Location Indices (Checking proper Section, Group) + ASSERT_EQ(this->m_portOutInvokes[0][1], 2); + ASSERT_EQ(this->m_portOutInvokes[1][1], 2); + ASSERT_EQ(this->m_portOutInvokes[0][2], 3); + ASSERT_EQ(this->m_portOutInvokes[1][3], 2); + ASSERT_EQ(this->m_portOutInvokes[0][3], 2); + ASSERT_EQ(this->m_portOutInvokes[1][2], 0); + + // Pkt 4 on section 1 (Unchanged since T = 0) + // Pkt 4 on section 1 sent recently with a delta of 7 + ASSERT_from_PktSend(0, comBuff, static_cast(7)); + + this->clearHistory(); + + // T = 8-11, Expecting No Updates + for (U8 trial = 8; trial < 12; trial++) { + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + + ASSERT_FROM_PORT_HISTORY_SIZE(0); + ASSERT_from_PktSend_SIZE(0); + + // Packet Location Indices (Checking proper Section, Group) + ASSERT_EQ(this->m_portOutInvokes[0][1], 2); + ASSERT_EQ(this->m_portOutInvokes[1][1], 2); + ASSERT_EQ(this->m_portOutInvokes[0][2], 3); + ASSERT_EQ(this->m_portOutInvokes[1][3], 2); + ASSERT_EQ(this->m_portOutInvokes[0][3], 2); + ASSERT_EQ(this->m_portOutInvokes[1][2], 0); + } + + buffer.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(111))); + this->invoke_to_TlmRecv(0, 10, time, buffer); + + this->clearHistory(); + + // T = 12 + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + + ASSERT_FROM_PORT_HISTORY_SIZE(5); + ASSERT_from_PktSend_SIZE(5); + + // Packet Location Indices (Checking proper Section, Group) + ASSERT_EQ(this->m_portOutInvokes[0][1], 3); + ASSERT_EQ(this->m_portOutInvokes[1][1], 3); + ASSERT_EQ(this->m_portOutInvokes[0][2], 4); + ASSERT_EQ(this->m_portOutInvokes[1][3], 3); + ASSERT_EQ(this->m_portOutInvokes[0][3], 3); + ASSERT_EQ(this->m_portOutInvokes[1][2], 0); + + // Pkt 1 + comBuff.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, + comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(4))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(111))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(22))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(3))); + + // Pkt 1 on section 0 sent recently with a delta of 9 + // Pkt 1 on section 1 sent recently with a delta of 10 + ASSERT_from_PktSend(0, comBuff, static_cast(9)); // Section 0 + ASSERT_from_PktSend(1, comBuff, static_cast(10)); // Section 1 + + // Pkt 2 + comBuff.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, + comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(8))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(111))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(22))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(3))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(4))); + + // Pkt 2 on section 0 sent recently with a delta of 8 + ASSERT_from_PktSend(2, comBuff, static_cast(8)); // Section 0 + + // Pkt 4 + comBuff.resetSer(); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, + comBuff.serializeFrom(static_cast(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(16))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(this->m_testTime)); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(111))); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, comBuff.serializeFrom(static_cast(2))); + + // Pkt 4 on section 0 sent recently with a delta of 6 + // Pkt 4 on section 1 sent recently with a delta of 5 + ASSERT_from_PktSend(3, comBuff, static_cast(6)); // Section 0 + ASSERT_from_PktSend(4, comBuff, static_cast(5)); // Section 1 +} + +//! Configure telemetry enable logic +//! +void TlmPacketizerTester ::advancedControlGroupTests(void) { + this->component.setPacketList(packetList2, ignore, 4); + this->m_primaryTestLock = false; + Fw::Time time; + Fw::TlmBuffer buffer; + + ASSERT_EQ(Fw::FW_SERIALIZE_OK, buffer.serializeFrom(static_cast(1))); + this->invoke_to_TlmRecv(0, 10, time, buffer); + + this->sendCmd_SET_LEVEL(0, 0, 1); + this->component.doDispatch(); + + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + + // Default ON_CHANGE Behavior + ASSERT_FROM_PORT_HISTORY_SIZE(3); + ASSERT_from_PktSend_SIZE(3); + this->clearHistory(); + + // Send a packet every time the port is invoked. + this->sendCmd_SET_GROUP_DELTAS(0, 0, 0, 1, Svc::TlmPacketizer_RateLogic::EVERY_MAX, 0, 0); + this->component.doDispatch(); + + this->clearHistory(); + + // Expect Packet Default Enabled + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + + ASSERT_FROM_PORT_HISTORY_SIZE(1); + ASSERT_from_PktSend_SIZE(1); + this->clearHistory(); + + // Disable this group on section 0 (primary) + this->sendCmd_ENABLE_GROUP(0, 0, 0, 1, Fw::Enabled::DISABLED); + this->component.doDispatch(); + // Expect No Packets + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + ASSERT_FROM_PORT_HISTORY_SIZE(0); + ASSERT_from_PktSend_SIZE(0); + + // Enable group on section, but disable section + this->sendCmd_ENABLE_GROUP(0, 0, 0, 1, Fw::Enabled::ENABLED); + this->component.doDispatch(); + this->sendCmd_ENABLE_SECTION(0, 0, 0, Fw::Enabled::DISABLED); + this->component.doDispatch(); + // Expect No Packets + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + ASSERT_FROM_PORT_HISTORY_SIZE(0); + ASSERT_from_PktSend_SIZE(0); + + // Enable Section by Port Invocation + this->sendCmd_ENABLE_SECTION(0, 0, 0, Fw::Enabled::ENABLED); + this->component.doDispatch(); + this->invoke_to_controlIn(0, 0, Fw::Enabled::ENABLED); + this->component.doDispatch(); + // Expect A Packet + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + ASSERT_FROM_PORT_HISTORY_SIZE(1); + ASSERT_from_PktSend_SIZE(1); + this->clearHistory(); + + // Disable section by port invocation, but Send Command Force Section + this->invoke_to_controlIn(0, 0, Fw::Enabled::DISABLED); + this->component.doDispatch(); + this->sendCmd_FORCE_GROUP(0, 0, 0, 1, Fw::Enabled::ENABLED); + this->component.doDispatch(); + // Expect A Packet + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + ASSERT_FROM_PORT_HISTORY_SIZE(1); + ASSERT_from_PktSend_SIZE(1); + this->clearHistory(); + + // Disable group, but keep force group command active + this->sendCmd_ENABLE_GROUP(0, 0, 0, 1, Fw::Enabled::DISABLED); + this->component.doDispatch(); + // Expect A Packet + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + ASSERT_FROM_PORT_HISTORY_SIZE(1); + ASSERT_from_PktSend_SIZE(1); + this->clearHistory(); + + // Disable Force Group, with Group Disabled and Section Disabled + this->sendCmd_FORCE_GROUP(0, 0, 0, 1, Fw::Enabled::DISABLED); + this->component.doDispatch(); + // Expect No Packets + this->invoke_to_Run(0, 0); + this->component.doDispatch(); + ASSERT_FROM_PORT_HISTORY_SIZE(0); + ASSERT_from_PktSend_SIZE(0); + this->clearHistory(); +} + // ---------------------------------------------------------------------- // Handlers for typed from ports // ---------------------------------------------------------------------- void TlmPacketizerTester ::from_PktSend_handler(const FwIndexType portNum, Fw::ComBuffer& data, U32 context) { + this->m_portOutInvokes[portNum / (MAX_CONFIGURABLE_TLMPACKETIZER_GROUP + 1)] + [portNum % (MAX_CONFIGURABLE_TLMPACKETIZER_GROUP + 1)]++; + if (this->m_primaryTestLock && portNum > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP * 1) { + return; + } this->pushFromPortEntry_PktSend(data, context); } @@ -958,7 +1527,8 @@ void TlmPacketizerTester ::from_pingOut_handler(const FwIndexType portNum, U32 k void TlmPacketizerTester ::connectPorts() { // PktSend - this->component.set_PktSend_OutputPort(0, this->get_from_PktSend(0)); + // this->component.set_PktSend_OutputPort(0, this->get_from_PktSend(0)); + // this->component.set_PktSend_OutputPort(1, this->get_from_PktSend(1)); // Run this->connect_to_Run(0, this->component.get_Run_InputPort(0)); @@ -995,6 +1565,14 @@ void TlmPacketizerTester ::connectPorts() { // TlmGet this->connect_to_TlmGet(0, this->component.get_TlmGet_InputPort(0)); + + for (FwIndexType index = 0; + index < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS * (MAX_CONFIGURABLE_TLMPACKETIZER_GROUP + 1); index++) { + this->component.set_PktSend_OutputPort(index, this->get_from_PktSend(index)); + } + + // controlIn + this->connect_to_controlIn(0, this->component.get_controlIn_InputPort(0)); } void TlmPacketizerTester::textLogIn(const FwEventIdType id, //!< The event ID @@ -1012,4 +1590,12 @@ void TlmPacketizerTester ::initComponents() { this->component.init(QUEUE_DEPTH, INSTANCE); } +void TlmPacketizerTester ::resetCounter(void) { + for (FwIndexType section = 0; section < NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS; section++) { + for (FwChanIdType group = 0; group < MAX_CONFIGURABLE_TLMPACKETIZER_GROUP; group++) { + this->m_portOutInvokes[section][group] = 0; + }; + } +}; + } // end namespace Svc diff --git a/Svc/TlmPacketizer/test/ut/TlmPacketizerTester.hpp b/Svc/TlmPacketizer/test/ut/TlmPacketizerTester.hpp index 3467731bce4..1a28e75a667 100644 --- a/Svc/TlmPacketizer/test/ut/TlmPacketizerTester.hpp +++ b/Svc/TlmPacketizer/test/ut/TlmPacketizerTester.hpp @@ -11,6 +11,7 @@ #ifndef TESTER_HPP #define TESTER_HPP +#include #include "Svc/TlmPacketizer/TlmPacketizer.hpp" #include "TlmPacketizerGTestBase.hpp" @@ -79,6 +80,14 @@ class TlmPacketizerTester : public TlmPacketizerGTestBase { //! void getChannelValueTest(void); + //! Configured tlm groups test + //! + void configuredTelemetryGroupsTests(void); + + //! Configure telemetry enable logic + //! + void advancedControlGroupTests(void); + private: // ---------------------------------------------------------------------- // Handlers for typed from ports @@ -116,6 +125,10 @@ class TlmPacketizerTester : public TlmPacketizerGTestBase { //! void initComponents(void); + //! Reset Counter + //! + void resetCounter(void); + private: // ---------------------------------------------------------------------- // Variables @@ -126,6 +139,10 @@ class TlmPacketizerTester : public TlmPacketizerGTestBase { TlmPacketizer component; Fw::Time m_testTime; //!< store test time for packets + + bool m_primaryTestLock{true}; //! Lock limited to entries from port 0 PktSend + U8 m_portOutInvokes[NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS][MAX_CONFIGURABLE_TLMPACKETIZER_GROUP + 1]{}; + // Svc::Queue m_portCalls{}; }; } // end namespace Svc diff --git a/Svc/TlmPacketizer/test/ut/main.cpp b/Svc/TlmPacketizer/test/ut/main.cpp index fba76bb7a21..c082847ed0a 100644 --- a/Svc/TlmPacketizer/test/ut/main.cpp +++ b/Svc/TlmPacketizer/test/ut/main.cpp @@ -74,6 +74,16 @@ TEST(TestNominal, TlmGetTest) { Svc::TlmPacketizerTester tester; tester.getChannelValueTest(); } +TEST(TestNominal, configuredTelemetryGroupsTests) { + TEST_CASE(100.1.9, "Configure Telem Send Levels and Rates"); + Svc::TlmPacketizerTester tester; + tester.configuredTelemetryGroupsTests(); +} +TEST(TestNominal, advancedControlGroupTests) { + TEST_CASE(100.1.10, "Control enable sections and groups"); + Svc::TlmPacketizerTester tester; + tester.advancedControlGroupTests(); +} int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); diff --git a/default/config/CMakeLists.txt b/default/config/CMakeLists.txt index 935d998034e..a3a2bfcadcb 100644 --- a/default/config/CMakeLists.txt +++ b/default/config/CMakeLists.txt @@ -18,6 +18,7 @@ register_fprime_config( "${CMAKE_CURRENT_LIST_DIR}/PlatformCfg.fpp" "${CMAKE_CURRENT_LIST_DIR}/PolyDbCfg.fpp" "${CMAKE_CURRENT_LIST_DIR}/VersionCfg.fpp" + "${CMAKE_CURRENT_LIST_DIR}/TlmPacketizerCfg.fpp" HEADERS "${CMAKE_CURRENT_LIST_DIR}/EventManagerCfg.hpp" "${CMAKE_CURRENT_LIST_DIR}/ActiveRateGroupCfg.hpp" diff --git a/default/config/TlmPacketizerCfg.fpp b/default/config/TlmPacketizerCfg.fpp new file mode 100644 index 00000000000..73a890ba8f2 --- /dev/null +++ b/default/config/TlmPacketizerCfg.fpp @@ -0,0 +1,13 @@ +# ====================================================================== +# TlmPacketizerCfg.fpp +# Constants for configuring TlmPacketizer Send Logic Levels +# ====================================================================== +module Svc { + @ The number of sections of ports (Primary = 0, Secondary = 1, etc...) + constant NUM_CONFIGURABLE_TLMPACKETIZER_SECTIONS = 3; + + @ Greatest packet group + constant MAX_CONFIGURABLE_TLMPACKETIZER_GROUP = 3; + + constant NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS = MAX_CONFIGURABLE_TLMPACKETIZER_GROUP + 1; +}