GCC Code Coverage Report


Directory: Svc/TlmPacketizer/
File: TlmPacketizer.cpp
Date: 2026-11-03 21:19:22
Exec Total Coverage
Lines: 448 472 94.9%
Functions: 34 34 100.0%
Branches: 432 510 84.7%

Line Branch Exec Source
1 // ======================================================================
2 // \title TlmPacketizerImpl.cpp
3 // \author tcanham
4 // \brief cpp file for TlmPacketizer component implementation class
5 //
6 // \copyright
7 // Copyright 2009-2015, by the California Institute of Technology.
8 // ALL RIGHTS RESERVED. United States Government Sponsorship
9 // acknowledged.
10
11 #include <Fw/Com/ComPacket.hpp>
12 #include <Fw/FPrimeBasicTypes.hpp>
13 #include <Fw/Prm/ParamValid.hpp>
14 #include <Svc/TlmPacketizer/TlmPacketizer.hpp>
15 #include <TlmPacketizerConfig/FppConstantsAc.hpp>
16 #include <cstring>
17 #include <limits>
18
19 namespace Svc {
20
21 const TlmPacketizer_TelemetrySendPortMap TlmPacketizer::TELEMETRY_SEND_PORT_MAP = {};
22
23 static_assert(Svc::TelemetrySection::NUM_SECTIONS >= 1, "At least one telemetry section is required");
24
25 // Keeps every offset a ComBuffer can hold representable in the offset type; setPacketList additionally asserts
26 // at runtime that no offset equals the NOT_IN_PACKET sentinel (the type's maximum)
27 static_assert(FW_COM_BUFFER_MAX_SIZE <= std::numeric_limits<FwTlmPacketizeOffsetType>::max(),
28 "FwTlmPacketizeOffsetType must span an entire ComBuffer");
29
30 static_assert(static_cast<U8>(Fw::FW_DESERIALIZE_BUFFER_EMPTY) == Fw::DeserialStatus::BUFFER_EMPTY and
31 static_cast<U8>(Fw::FW_DESERIALIZE_FORMAT_ERROR) == Fw::DeserialStatus::FORMAT_ERROR and
32 static_cast<U8>(Fw::FW_DESERIALIZE_SIZE_MISMATCH) == Fw::DeserialStatus::SIZE_MISMATCH and
33 static_cast<U8>(Fw::FW_DESERIALIZE_TYPE_MISMATCH) == Fw::DeserialStatus::TYPE_MISMATCH,
34 "Fw::DeserialStatus must mirror Fw::SerializeStatus values");
35
36 // ----------------------------------------------------------------------
37 // Construction, initialization, and destruction
38 // ----------------------------------------------------------------------
39
40 35 TlmPacketizer ::TlmPacketizer(const char* const compName)
41
12/18
✓ Branch 20 taken 1750 times.
✓ Branch 23 taken 1750 times.
✓ Branch 24 taken 35 times.
✓ Branch 30 taken 35 times.
✓ Branch 41 taken 35 times.
✓ Branch 48 taken 35 times.
✓ Branch 55 taken 35 times.
✓ Branch 63 taken 3430 times.
✓ Branch 64 taken 70 times.
✓ Branch 65 taken 35 times.
✓ Branch 66 taken 35 times.
✓ Branch 75 taken 35 times.
✗ Branch 81 not taken.
✗ Branch 82 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 101 not taken.
✗ Branch 102 not taken.
5250 : TlmPacketizerComponentBase(compName), m_numPackets(0), m_configured(false), m_numChannels(0) {
42 // Register self as parameter delegate
43
1/1
✓ Branch 7 taken 35 times.
35 this->registerExternalParameters(this);
44 // clear missing tlm channel check
45
2/2
✓ Branch 0 taken 875 times.
✓ Branch 1 taken 35 times.
910 for (FwChanIdType entry = 0; entry < TLMPACKETIZER_MAX_MISSING_TLM_CHECK; entry++) {
46 875 this->m_missTlmCheck[entry].checked = false;
47 875 this->m_missTlmCheck[entry].id = 0;
48 }
49
50 // clear packet buffers
51
2/2
✓ Branch 0 taken 1750 times.
✓ Branch 1 taken 35 times.
1785 for (FwChanIdType buffer = 0; buffer < MAX_PACKETIZER_PACKETS; buffer++) {
52 1750 this->m_fillBuffers[buffer].updated = false;
53 }
54
55 static_assert(NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS == MAX_CONFIGURABLE_TLMPACKETIZER_GROUP + 1,
56 "NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS MUST BE MAX_CONFIGURABLE_TLMPACKETIZER_GROUP + 1");
57 35 }
58
59
3/4
✓ Branch 37 taken 35 times.
✗ Branch 38 not taken.
✓ Branch 44 taken 1750 times.
✓ Branch 45 taken 35 times.
3570 TlmPacketizer ::~TlmPacketizer() {}
60
61 27 void TlmPacketizer::setPacketList(const TlmPacketizerPacketList& packetList,
62 const Svc::TlmPacketizerPacket& ignoreList,
63 const FwChanIdType startLevel) {
64 // Ignore list may be nullptr as long as numEntries is 0. Providing an ignore list with numEntries 0 disables
65 // functionality for two reasons:
66 // 1. There are no ignored channels as configured by FPP.
67 // 2. Ignore functionality is intentionally disabled by project where nullptr was intentionally supplied.
68 27 FW_ASSERT(ignoreList.list || ignoreList.numEntries == 0);
69 27 FW_ASSERT(packetList.numEntries <= MAX_PACKETIZER_PACKETS, static_cast<FwAssertArgType>(packetList.numEntries));
70
71 // Reset key data members incase of reentrant calls
72 27 this->m_numChannels = 0;
73 27 this->m_channelIndices.clear();
74 27 this->m_configured = false;
75
76 // validate packet sizes against maximum com buffer size and populate hash
77 // table
78 27 FwChanIdType maxLevel = 0;
79
2/2
✓ Branch 2 taken 105 times.
✓ Branch 3 taken 27 times.
132 for (FwChanIdType pktEntry = 0; pktEntry < packetList.numEntries; pktEntry++) {
80 // Initial size is packetized telemetry descriptor + size of time tag + sizeof packet ID
81 105 FwSizeType packetLen =
82 sizeof(FwPacketDescriptorType) + Fw::Time::SERIALIZED_SIZE + sizeof(FwTlmPacketizeIdType);
83 // A packet with no channels is autocoded with a nullptr list and is permitted
84 105 FW_ASSERT(packetList.list[pktEntry]->list != nullptr || packetList.list[pktEntry]->numEntries == 0,
85 static_cast<FwAssertArgType>(pktEntry));
86 // add up entries for each defined packet
87
2/2
✓ Branch 5 taken 226 times.
✓ Branch 6 taken 105 times.
331 for (FwChanIdType tlmEntry = 0; tlmEntry < packetList.list[pktEntry]->numEntries; tlmEntry++) {
88 226 FwChanIdType id = packetList.list[pktEntry]->list[tlmEntry].id;
89 226 const FwSizeType channelSize = packetList.list[pktEntry]->list[tlmEntry].size;
90 226 FwSizeType entryIndex = 0;
91
3/3
✓ Branch 4 taken 226 times.
✓ Branch 13 taken 151 times.
✓ Branch 14 taken 75 times.
226 if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
92 // New channel - allocate a slot and initialize offsets to NOT_IN_PACKET
93 151 entryIndex = this->m_numChannels++;
94
1/1
✓ Branch 4 taken 151 times.
151 this->m_channels[entryIndex].id = id;
95
1/1
✓ Branch 4 taken 151 times.
151 this->m_channels[entryIndex].hasValue = false;
96
1/1
✓ Branch 4 taken 151 times.
151 this->m_channels[entryIndex].channelSize = channelSize;
97
2/2
✓ Branch 0 taken 7550 times.
✓ Branch 1 taken 151 times.
7701 for (FwChanIdType pktOffsetEntry = 0; pktOffsetEntry < MAX_PACKETIZER_PACKETS; pktOffsetEntry++) {
98
1/1
✓ Branch 4 taken 7550 times.
7550 this->m_channels[entryIndex].packetOffset[pktOffsetEntry] = NOT_IN_PACKET;
99 }
100
1/1
✓ Branch 4 taken 151 times.
151 const Fw::Success insertStatus = this->m_channelIndices.insert(id, entryIndex);
101 151 FW_ASSERT(insertStatus == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(insertStatus));
102 151 } else {
103 // Existing channel - a channel ID may repeat across packets, but its definition (size) must match.
104 // A conflicting size would corrupt the packet offsets computed from the earlier definition and
105 // overflow the fill buffer during TlmRecv/TlmGet copies, so reject the misconfiguration here.
106 75 FW_ASSERT(this->m_channels[entryIndex].channelSize == channelSize, static_cast<FwAssertArgType>(id),
107 static_cast<FwAssertArgType>(channelSize),
108 static_cast<FwAssertArgType>(this->m_channels[entryIndex].channelSize));
109 }
110 // not ignored channel - update entry in place via reference
111
1/1
✓ Branch 4 taken 226 times.
226 TlmEntry& entry = this->m_channels[entryIndex];
112 226 entry.ignored = false;
113 226 entry.channelSize = channelSize;
114 // the offset into the buffer will be the current packet length
115 // the offset must fit within FwTlmPacketizeOffsetType and must not equal NOT_IN_PACKET
116 226 FW_ASSERT(packetLen < NOT_IN_PACKET, static_cast<FwAssertArgType>(packetLen));
117 226 entry.packetOffset[pktEntry] = static_cast<FwTlmPacketizeOffsetType>(packetLen);
118
119 226 packetLen += entry.channelSize;
120
121 } // end channel in packet
122 105 FW_ASSERT(packetLen <= FW_COM_BUFFER_MAX_SIZE, static_cast<FwAssertArgType>(packetLen),
123 static_cast<FwAssertArgType>(pktEntry));
124 // clear contents
125
1/2
✗ Branch 7 not taken.
✓ Branch 8 taken 105 times.
105 (void)memset(this->m_fillBuffers[pktEntry].buffer.getBuffAddr(), 0, static_cast<size_t>(packetLen));
126 // serialize packet descriptor and packet ID now since it will always be the same
127 105 Fw::SerializeStatus stat = this->m_fillBuffers[pktEntry].buffer.serializeFrom(
128 static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM));
129 105 FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
130 105 stat = this->m_fillBuffers[pktEntry].buffer.serializeFrom(packetList.list[pktEntry]->id);
131 105 FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
132 // set packet buffer length
133 105 stat = this->m_fillBuffers[pktEntry].buffer.setBuffLen(packetLen);
134 105 FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
135 // save ID
136 105 this->m_fillBuffers[pktEntry].id = packetList.list[pktEntry]->id;
137 // save level
138 105 this->m_fillBuffers[pktEntry].level = packetList.list[pktEntry]->level;
139 // store max level
140
2/2
✓ Branch 5 taken 52 times.
✓ Branch 6 taken 53 times.
105 if (packetList.list[pktEntry]->level > maxLevel) {
141 52 maxLevel = packetList.list[pktEntry]->level;
142 }
143
144 } // end packet list
145 27 FW_ASSERT(maxLevel <= MAX_CONFIGURABLE_TLMPACKETIZER_GROUP, static_cast<FwAssertArgType>(maxLevel));
146
147 // This section adds entries in the map for channels that are intended to be ignored. When the user supplies
148 // a list with no length, this loop is skipped. To turn-off ignoring of channels, the user can provide a null
149 // list with 0 length.
150
2/2
✓ Branch 2 taken 44 times.
✓ Branch 3 taken 27 times.
71 for (FwChanIdType channelEntry = 0; channelEntry < ignoreList.numEntries; channelEntry++) {
151 44 FwChanIdType id = ignoreList.list[channelEntry].id;
152 44 FwSizeType entryIndex = 0;
153
2/3
✓ Branch 4 taken 44 times.
✓ Branch 13 taken 44 times.
✗ Branch 14 not taken.
44 if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
154 // New channel - allocate a slot and initialize offsets to NOT_IN_PACKET
155 44 entryIndex = this->m_numChannels++;
156
1/1
✓ Branch 4 taken 44 times.
44 this->m_channels[entryIndex].id = id;
157
1/1
✓ Branch 4 taken 44 times.
44 this->m_channels[entryIndex].hasValue = false;
158
2/2
✓ Branch 0 taken 2200 times.
✓ Branch 1 taken 44 times.
2244 for (FwChanIdType pktOffsetEntry = 0; pktOffsetEntry < MAX_PACKETIZER_PACKETS; pktOffsetEntry++) {
159
1/1
✓ Branch 4 taken 2200 times.
2200 this->m_channels[entryIndex].packetOffset[pktOffsetEntry] = NOT_IN_PACKET;
160 }
161
1/1
✓ Branch 4 taken 44 times.
44 const Fw::Success insertStatus = this->m_channelIndices.insert(id, entryIndex);
162 44 FW_ASSERT(insertStatus == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(insertStatus));
163 44 } else {
164 // Ensure it is a duplicate in the ignore list, not a duplicate of a valid channel
165 ✗ FW_ASSERT(this->m_channels[entryIndex].ignored, static_cast<FwAssertArgType>(id));
166 }
167 // is ignored channel - update entry in place via reference
168
1/1
✓ Branch 4 taken 44 times.
44 TlmEntry& entry = this->m_channels[entryIndex];
169 44 entry.ignored = true;
170 44 entry.channelSize = ignoreList.list[channelEntry].size;
171 } // end ignore list
172
173 // store number of packets
174 27 this->m_numPackets = packetList.numEntries;
175
176 // indicate configured
177 27 this->m_configured = true;
178 27 }
179
180 // ----------------------------------------------------------------------
181 // Handler implementations for user-defined typed input ports
182 // ----------------------------------------------------------------------
183
184 200 void TlmPacketizer ::TlmRecv_handler(const FwIndexType portNum,
185 FwChanIdType id,
186 Fw::Time& timeTag,
187 Fw::TlmBuffer& val) {
188 200 FW_ASSERT(this->m_configured);
189 200 FwSizeType entryIndex = 0;
190
191 // Search to see if the channel is being tracked
192
3/3
✓ Branch 4 taken 200 times.
✓ Branch 13 taken 51 times.
✓ Branch 14 taken 149 times.
200 if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
193 // channel not part of a packet and not ignored
194
1/1
✓ Branch 4 taken 51 times.
51 this->missingChannel(id);
195 51 return;
196 }
197
198
1/1
✓ Branch 4 taken 149 times.
149 TlmEntry& entry = this->m_channels[entryIndex];
199
200 // check to see if the channel is ignored. If so, just return.
201
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 149 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 148 times.
149 if (entry.ignored) {
202 1 return;
203 }
204
205 // An update larger than the configured channel size would overrun the packet
206 // fill buffer. Values may arrive from external sources (e.g. a hub bridging
207 // another address space), so reject rather than assert.
208
3/3
✓ Branch 5 taken 148 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 147 times.
148 if (val.getSize() > entry.channelSize) {
209
2/2
✓ Branch 11 taken 1 times.
✓ Branch 14 taken 1 times.
1 this->log_WARNING_HI_OversizedChannel(id, val.getSize(), entry.channelSize);
210 1 return;
211 }
212
213 // copy telemetry value into active buffers; hasValue written in-place via reference
214
2/2
✓ Branch 0 taken 7350 times.
✓ Branch 1 taken 147 times.
7497 for (FwChanIdType pkt = 0; pkt < MAX_PACKETIZER_PACKETS; pkt++) {
215 // check if current packet has this channel
216
2/2
✓ Branch 3 taken 226 times.
✓ Branch 4 taken 7124 times.
7350 if (entry.packetOffset[pkt] != NOT_IN_PACKET) {
217 // get destination address
218
1/1
✓ Branch 5 taken 226 times.
226 Os::ScopeLock lock(this->m_lock);
219 226 this->m_fillBuffers[pkt].updated = true;
220
1/1
✓ Branch 8 taken 226 times.
226 this->m_fillBuffers[pkt].latestTime = timeTag;
221 226 U8* ptr = &this->m_fillBuffers[pkt].buffer.getBuffAddr()[entry.packetOffset[pkt]];
222
223
3/5
✓ Branch 5 taken 226 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 226 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 226 times.
226 (void)memcpy(ptr, val.getBuffAddr(), static_cast<size_t>(val.getSize()));
224 // set under the lock, after the copy, so TlmGet cannot see a value-less VALID entry
225 226 entry.hasValue = true;
226 226 }
227 }
228 }
229
230 1 void TlmPacketizer ::configureSectionGroupRate_handler(FwIndexType portNum,
231 const Svc::TelemetrySection& section,
232 FwChanIdType tlmGroup,
233 const Svc::RateLogic& rateLogic,
234 U32 minDelta,
235 U32 maxDelta) {
236 1 this->configureSectionGroupRate(section, tlmGroup, rateLogic, minDelta, maxDelta);
237 1 }
238
239 //! Handler for input port TlmGet
240 7 Fw::TlmValid TlmPacketizer ::TlmGet_handler(FwIndexType portNum, //!< The port number
241 FwChanIdType id, //!< Telemetry Channel ID
242 Fw::Time& timeTag, //!< Time Tag
243 Fw::TlmBuffer& val //!< Buffer containing serialized telemetry value.
244 //!< Size set to 0 if channel not found.
245 ) {
246 7 FW_ASSERT(this->m_configured);
247 7 FwSizeType entryIndex = 0;
248
249 // Search to see if the channel is being tracked
250
3/3
✓ Branch 4 taken 7 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 6 times.
7 if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
251 // channel not part of a packet and not ignored
252
1/1
✓ Branch 4 taken 1 times.
1 this->missingChannel(id);
253
1/1
✓ Branch 5 taken 1 times.
1 val.resetSer();
254
1/1
✓ Branch 1 taken 1 times.
1 return Fw::TlmValid::INVALID;
255 }
256
1/1
✓ Branch 4 taken 6 times.
6 const TlmEntry& entry = this->m_channels[entryIndex];
257
258 // check to see if the channel is ignored. If so, just return, as
259 // we don't store the bytes of ignored channels
260
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 5 times.
6 if (entry.ignored) {
261
1/1
✓ Branch 5 taken 1 times.
1 val.resetSer();
262
1/1
✓ Branch 1 taken 1 times.
1 return Fw::TlmValid::INVALID;
263 }
264
265
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 4 times.
5 if (!entry.hasValue) {
266 // haven't received a value yet for this entry.
267
1/1
✓ Branch 5 taken 1 times.
1 val.resetSer();
268
1/1
✓ Branch 1 taken 1 times.
1 return Fw::TlmValid::INVALID;
269 }
270
271 // make sure we have enough space to store this entry in our buf
272 4 FW_ASSERT(entry.channelSize <= val.getCapacity(), static_cast<FwAssertArgType>(entry.channelSize),
273 static_cast<FwAssertArgType>(val.getCapacity()));
274
275 // okay, we have the matching entry.
276 // go over each packet and find the first one which stores this channel
277
278
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 for (FwChanIdType pkt = 0; pkt < MAX_PACKETIZER_PACKETS; pkt++) {
279 // check if current packet has this channel
280
2/2
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 1 times.
5 if (entry.packetOffset[pkt] != NOT_IN_PACKET) {
281 // okay, it has the channel. copy chan val into the tlm buf
282
1/1
✓ Branch 5 taken 4 times.
4 Os::ScopeLock lock(this->m_lock);
283
1/1
✓ Branch 8 taken 4 times.
4 timeTag = this->m_fillBuffers[pkt].latestTime;
284 4 U8* ptr = &this->m_fillBuffers[pkt].buffer.getBuffAddr()[entry.packetOffset[pkt]];
285
2/4
✗ Branch 7 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4 times.
4 (void)memcpy(val.getBuffAddr(), ptr, static_cast<size_t>(entry.channelSize));
286 // set buf len to the channelSize. keep in mind, this is the MAX serialized size of the channel.
287 // so we may actually be filling val with some junk after the value of the channel.
288
1/1
✓ Branch 7 taken 4 times.
4 const Fw::SerializeStatus setStatus = val.setBuffLen(entry.channelSize);
289 4 FW_ASSERT(setStatus == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(setStatus));
290
1/1
✓ Branch 1 taken 4 times.
4 return Fw::TlmValid::VALID;
291 4 }
292 }
293
294 // did not find a packet which stores this channel.
295 // coding error, this was not an ignored channel so it must be in a packet somewhere
296 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(entry.id));
297 // TPP (tim paranoia principle)
298 ✗ val.resetSer();
299 ✗ return Fw::TlmValid::INVALID;
300 }
301
302 51 void TlmPacketizer ::Run_handler(const FwIndexType portNum, U32 context) {
303 51 FW_ASSERT(this->m_configured);
304
305
2/2
✓ Branch 4 taken 167 times.
✓ Branch 5 taken 51 times.
218 for (FwChanIdType pkt = 0; pkt < this->m_numPackets; pkt++) {
306 // Local flags to track which sections require a packet dispatch
307 167 bool sectionNeedsSend[TelemetrySection::NUM_SECTIONS] = {false};
308 167 bool anySectionNeedsSend = false;
309
310 // Lock only to capture the update status and group, and reset the fill buffer flag.
311 167 bool isNewData = false;
312 167 FwChanIdType entryGroup = 0;
313 {
314
1/1
✓ Branch 5 taken 167 times.
167 Os::ScopeLock lock(this->m_lock);
315
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 167 times.
167 isNewData = this->m_fillBuffers[pkt].updated;
316 167 entryGroup = this->m_fillBuffers[pkt].level;
317 167 this->m_fillBuffers[pkt].updated = false;
318 167 }
319
320
2/2
✓ Branch 0 taken 334 times.
✓ Branch 1 taken 167 times.
501 for (FwIndexType section = 0; section < TelemetrySection::NUM_SECTIONS; section++) {
321 334 PktSendCounters& pktEntryFlags = this->m_packetFlags[static_cast<FwSizeType>(section)][pkt];
322 // Group policy with the packet's overridden fields copied in
323
1/1
✓ Branch 2 taken 334 times.
334 const Svc::TlmSendConfig effectiveEntryConfig = this->effectiveConfig(section, pkt);
324
325 // Packet is updated and not REQUESTED (Keep REQUESTED marking to bypass disable checks)
326
4/4
✓ Branch 0 taken 236 times.
✓ Branch 1 taken 98 times.
✓ Branch 4 taken 233 times.
✓ Branch 5 taken 3 times.
334 if (isNewData && pktEntryFlags.updateFlag != UpdateFlag::REQUESTED) {
327 233 pktEntryFlags.updateFlag = UpdateFlag::NEW;
328 }
329
330 /* Base conditions for sending
331 1. Output port is connected
332 2. The packet was requested (Override Checks).
333
334 If the packet wasn't requested:
335 3. The effective send state allows sending (packet -> group -> section enable)
336 4. The packet has data (marked updated in the past or new)
337 */
338
3/4
✓ Branch 5 taken 334 times.
✓ Branch 8 taken 334 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 334 times.
334 if (!this->isConnected_PktSend_OutputPort(this->sectionGroupToPort(section, entryGroup))) {
339 ✗ continue;
340 }
341
342
2/2
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 331 times.
334 if (pktEntryFlags.updateFlag == UpdateFlag::REQUESTED) {
343 3 sectionNeedsSend[section] = true;
344 } else {
345
4/4
✓ Branch 7 taken 331 times.
✓ Branch 11 taken 331 times.
✓ Branch 17 taken 73 times.
✓ Branch 18 taken 258 times.
331 if (not this->sendAllowed(effectiveEntryConfig.get_sendState(), section)) {
346 73 continue;
347 }
348
2/2
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 248 times.
258 if (pktEntryFlags.updateFlag == UpdateFlag::NEVER_UPDATED) {
349 10 continue; // Avoid No Data
350 }
351 }
352
353 // Update Counter, prevent overflow.
354
2/2
✓ Branch 2 taken 100 times.
✓ Branch 3 taken 151 times.
251 if (pktEntryFlags.prevSentCounter < std::numeric_limits<U32>::max()) {
355 100 pktEntryFlags.prevSentCounter++;
356 }
357
358 /*
359 1. Packet has been updated
360 2. Group Logic includes checking MIN
361 3. Packet sent counter at MIN
362 */
363
1/2
✓ Branch 2 taken 201 times.
✗ Branch 3 not taken.
452 if (pktEntryFlags.updateFlag == UpdateFlag::NEW and
364
5/6
✓ Branch 0 taken 201 times.
✓ Branch 1 taken 50 times.
✓ Branch 8 taken 201 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 201 times.
✓ Branch 11 taken 50 times.
452 effectiveEntryConfig.get_rates().get_rateLogic() != Svc::RateLogic::EVERY_MAX and
365 201 pktEntryFlags.prevSentCounter >= effectiveEntryConfig.get_rates().get_min()) {
366 201 sectionNeedsSend[section] = true;
367 }
368
369 /*
370 1. Group Logic includes checking MAX
371 2. Packet set counter is at MAX
372 */
373
5/6
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 248 times.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✓ Branch 11 taken 248 times.
254 if (effectiveEntryConfig.get_rates().get_rateLogic() != Svc::RateLogic::ON_CHANGE_MIN and
374 3 pktEntryFlags.prevSentCounter >= effectiveEntryConfig.get_rates().get_max()) {
375 3 sectionNeedsSend[section] = true;
376 }
377
378
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 251 times.
✓ Branch 3 taken 207 times.
✓ Branch 4 taken 44 times.
251 if (sectionNeedsSend[section]) {
379 207 anySectionNeedsSend = true;
380 }
381 334 }
382
383 // Only perform the buffer copy if at least one section needs to send.
384
2/2
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 54 times.
167 if (anySectionNeedsSend) {
385 // Snapshot only the packet data under the lock
386
1/1
✓ Branch 2 taken 113 times.
113 Fw::ComBuffer sendBuffer;
387
1/1
✓ Branch 2 taken 113 times.
113 Fw::Time sendTime;
388 {
389
1/1
✓ Branch 5 taken 113 times.
113 Os::ScopeLock lock(this->m_lock);
390
1/1
✓ Branch 6 taken 113 times.
113 sendBuffer = this->m_fillBuffers[pkt].buffer;
391
1/1
✓ Branch 6 taken 113 times.
113 sendTime = this->m_fillBuffers[pkt].latestTime;
392 113 }
393
394 // serialize time into time offset in packet
395 113 Fw::ExternalSerializeBuffer buff(
396 113 &sendBuffer.getBuffAddr()[sizeof(FwPacketDescriptorType) + sizeof(FwTlmPacketizeIdType)],
397
1/1
✓ Branch 2 taken 113 times.
113 Fw::Time::SERIALIZED_SIZE);
398
1/1
✓ Branch 2 taken 113 times.
113 const Fw::SerializeStatus timeStatus = buff.serializeFrom(sendTime);
399 113 FW_ASSERT(Fw::FW_SERIALIZE_OK == timeStatus, static_cast<FwAssertArgType>(timeStatus));
400
401
2/2
✓ Branch 0 taken 226 times.
✓ Branch 1 taken 113 times.
339 for (FwIndexType section = 0; section < TelemetrySection::NUM_SECTIONS; section++) {
402
3/4
✗ Branch 1 not taken.
✓ Branch 2 taken 226 times.
✓ Branch 3 taken 207 times.
✓ Branch 4 taken 19 times.
226 if (sectionNeedsSend[section]) {
403 207 PktSendCounters& pktEntryFlags = this->m_packetFlags[section][pkt];
404
1/1
✓ Branch 1 taken 207 times.
207 FwIndexType outIndex = this->sectionGroupToPort(section, entryGroup);
405
406
1/1
✓ Branch 6 taken 207 times.
207 this->PktSend_out(outIndex, sendBuffer, pktEntryFlags.prevSentCounter);
407
408 207 pktEntryFlags.prevSentCounter = 0;
409 207 pktEntryFlags.updateFlag = UpdateFlag::PAST;
410 }
411 }
412 113 }
413 }
414 51 }
415
416 2 void TlmPacketizer ::controlIn_handler(FwIndexType portNum,
417 const Svc::TelemetrySection& section,
418 const Fw::Enabled& enabled) {
419 // NUM_SECTIONS is an enum constant (not a standalone constant), so isValid() accepts it.
420 // The explicit bounds check prevents an out-of-bounds write to m_sectionEnabled.
421
4/8
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 2 times.
✗ Branch 19 not taken.
2 if (section.isValid() && section < TelemetrySection::NUM_SECTIONS && enabled.isValid()) {
422 2 (void)(this->m_sectionEnabled[static_cast<FwSizeType>(section)] = enabled);
423 } else {
424 ✗ this->log_WARNING_LO_SectionUnconfigurable(section, enabled);
425 }
426 2 }
427
428 1 void TlmPacketizer ::pingIn_handler(const FwIndexType portNum, U32 key) {
429 // return key
430 1 this->pingOut_out(0, key);
431 1 }
432
433 // ----------------------------------------------------------------------
434 // Command handler implementations
435 // ----------------------------------------------------------------------
436
437 3 void TlmPacketizer ::SET_LEVEL_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, FwChanIdType level) {
438
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (level > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) {
439 1 this->log_WARNING_LO_MaxLevelExceed(level, MAX_CONFIGURABLE_TLMPACKETIZER_GROUP);
440
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
441 1 return;
442 }
443
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (FwIndexType section = 0; section < TelemetrySection::NUM_SECTIONS; section++) {
444
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
20 for (FwChanIdType group = 0; group < NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS; group++) {
445
2/2
✓ Branch 13 taken 8 times.
✓ Branch 14 taken 8 times.
16 this->m_groupConfigs[static_cast<FwSizeType>(section)][group].set_sendState(
446 group <= level ? Svc::TlmSendState::INHERIT_FROM_PARENT : Svc::TlmSendState::SEND_NEVER);
447 }
448 }
449
2/2
✓ Branch 6 taken 2 times.
✓ Branch 12 taken 2 times.
2 this->tlmWrite_GroupConfigs(this->m_groupConfigs);
450 2 this->log_ACTIVITY_HI_LevelSet(level);
451
2/2
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
2 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
452 }
453
454 4 void TlmPacketizer ::SEND_PKT_cmdHandler(const FwOpcodeType opCode,
455 const U32 cmdSeq,
456 const U32 id,
457 const Svc::TelemetrySection& section) {
458 4 FwChanIdType pkt = 0;
459
3/3
✓ Branch 4 taken 4 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 3 times.
4 if (this->resolvePacket(section, id, pkt) != PacketStatus::OK) {
460
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
461 1 return;
462 }
463
464 {
465
1/1
✓ Branch 5 taken 3 times.
3 Os::ScopeLock lock(this->m_lock);
466 3 this->m_fillBuffers[pkt].updated = true;
467
2/2
✓ Branch 3 taken 3 times.
✓ Branch 13 taken 3 times.
3 this->m_fillBuffers[pkt].latestTime = this->getTime();
468 3 }
469
470 3 this->m_packetFlags[section][pkt].updateFlag = UpdateFlag::REQUESTED;
471
472
1/1
✓ Branch 5 taken 3 times.
3 this->log_ACTIVITY_HI_PacketSent(id);
473
2/2
✓ Branch 6 taken 3 times.
✓ Branch 9 taken 3 times.
3 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
474 }
475
476 75 void TlmPacketizer ::ENABLE_SECTION_cmdHandler(FwOpcodeType opCode,
477 U32 cmdSeq,
478 const Svc::TelemetrySection& section,
479 const Fw::Enabled& enable) {
480 75 FW_ASSERT(section.isValid());
481 75 FW_ASSERT(enable.isValid());
482
3/6
✓ Branch 4 taken 75 times.
✗ Branch 5 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 75 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 75 times.
75 if (section < 0 or section >= TelemetrySection::NUM_SECTIONS) {
483 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
484 ✗ return;
485 }
486 75 (void)(this->m_sectionEnabled[section] = enable);
487
2/2
✓ Branch 6 taken 75 times.
✓ Branch 12 taken 75 times.
75 this->tlmWrite_SectionEnabled(this->m_sectionEnabled);
488
2/2
✓ Branch 6 taken 75 times.
✓ Branch 9 taken 75 times.
75 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
489 }
490
491 292 void TlmPacketizer ::CONFIGURE_GROUP_SEND_cmdHandler(FwOpcodeType opCode,
492 U32 cmdSeq,
493 const Svc::TelemetrySection& section,
494 FwChanIdType tlmGroup,
495 const Svc::TlmSendState& sendState) {
496 292 FW_ASSERT(section.isValid());
497
6/8
✓ Branch 4 taken 292 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 292 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 291 times.
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 291 times.
292 if (section < 0 or section >= TelemetrySection::NUM_SECTIONS or tlmGroup > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) {
498
2/2
✓ Branch 6 taken 1 times.
✓ Branch 9 taken 1 times.
1 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
499 1 return;
500 }
501 291 this->m_groupConfigs[section][tlmGroup].set_sendState(sendState);
502
2/2
✓ Branch 6 taken 291 times.
✓ Branch 12 taken 291 times.
291 this->tlmWrite_GroupConfigs(this->m_groupConfigs);
503 291 this->log_ACTIVITY_HI_GroupSendStateSet(section, tlmGroup, sendState);
504
2/2
✓ Branch 6 taken 291 times.
✓ Branch 9 taken 291 times.
291 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
505 }
506
507 282 void TlmPacketizer ::CONFIGURE_GROUP_RATES_cmdHandler(FwOpcodeType opCode,
508 U32 cmdSeq,
509 const Svc::TelemetrySection& section,
510 FwChanIdType tlmGroup,
511 const Svc::RateLogic& rateLogic,
512 U32 minDelta,
513 U32 maxDelta) {
514 282 FW_ASSERT(section.isValid());
515 282 FW_ASSERT(rateLogic.isValid());
516
4/8
✓ Branch 4 taken 282 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 282 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 282 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 282 times.
282 if (section < 0 or section >= TelemetrySection::NUM_SECTIONS or tlmGroup > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) {
517 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
518 ✗ return;
519 }
520 282 this->configureSectionGroupRate(section, tlmGroup, rateLogic, minDelta, maxDelta);
521
2/2
✓ Branch 6 taken 282 times.
✓ Branch 9 taken 282 times.
282 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
522 }
523
524 5 void TlmPacketizer ::GET_PACKET_CONFIG_cmdHandler(FwOpcodeType opCode,
525 U32 cmdSeq,
526 const Svc::TelemetrySection& section,
527 U32 packetId) {
528 5 FwChanIdType pkt = 0;
529
1/1
✓ Branch 4 taken 5 times.
5 const PacketStatus status = this->resolvePacket(section, packetId, pkt);
530
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if (status == PacketStatus::OK) {
531
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 const FwSizeType sectionIndex = static_cast<FwSizeType>(section.e);
532
3/3
✓ Branch 10 taken 3 times.
✓ Branch 16 taken 3 times.
✓ Branch 20 taken 3 times.
6 this->log_ACTIVITY_HI_PacketConfigReport(section, packetId, this->m_packetOverrides[sectionIndex][pkt],
533
2/3
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✓ Branch 7 taken 3 times.
6 this->effectiveConfig(static_cast<FwIndexType>(section.e), pkt));
534 }
535
2/2
✓ Branch 6 taken 5 times.
✓ Branch 9 taken 5 times.
5 this->cmdResponse_out(opCode, cmdSeq, TlmPacketizer::toCmdResponse(status));
536 5 }
537
538 16 void TlmPacketizer ::OVERRIDE_PACKET_SEND_cmdHandler(FwOpcodeType opCode,
539 U32 cmdSeq,
540 const Svc::TelemetrySection& section,
541 U32 packetId,
542 const Svc::TlmSendState& sendState) {
543 16 const Svc::PacketOverrideConfig requested(Svc::PacketOverrideType::OVERRIDE_SEND,
544
3/3
✓ Branch 3 taken 16 times.
✓ Branch 11 taken 16 times.
✓ Branch 15 taken 16 times.
16 Svc::TlmSendConfig(sendState, Svc::RateConfig()));
545
1/1
✓ Branch 6 taken 16 times.
32 this->cmdResponse_out(opCode, cmdSeq,
546
2/2
✓ Branch 4 taken 16 times.
✓ Branch 7 taken 16 times.
32 TlmPacketizer::toCmdResponse(this->overridePacket(section, packetId, requested)));
547 32 }
548
549 3 void TlmPacketizer ::OVERRIDE_PACKET_RATES_cmdHandler(FwOpcodeType opCode,
550 U32 cmdSeq,
551 const Svc::TelemetrySection& section,
552 U32 packetId,
553 const Svc::RateLogic& rateLogic,
554 U32 minDelta,
555 U32 maxDelta) {
556 3 const Svc::PacketOverrideConfig requested(
557 Svc::PacketOverrideType::OVERRIDE_RATES,
558
3/3
✓ Branch 7 taken 3 times.
✓ Branch 11 taken 3 times.
✓ Branch 15 taken 3 times.
3 Svc::TlmSendConfig(Svc::TlmSendState::INHERIT_FROM_PARENT, Svc::RateConfig(rateLogic, minDelta, maxDelta)));
559
1/1
✓ Branch 6 taken 3 times.
6 this->cmdResponse_out(opCode, cmdSeq,
560
2/2
✓ Branch 4 taken 3 times.
✓ Branch 7 taken 3 times.
6 TlmPacketizer::toCmdResponse(this->overridePacket(section, packetId, requested)));
561 6 }
562
563 5 void TlmPacketizer ::CLEAR_PACKET_OVERRIDE_cmdHandler(FwOpcodeType opCode,
564 U32 cmdSeq,
565 const Svc::TelemetrySection& section,
566 U32 packetId) {
567
2/2
✓ Branch 3 taken 5 times.
✓ Branch 7 taken 5 times.
5 const Svc::PacketOverrideConfig requested(Svc::PacketOverrideType::GROUP_DEFAULT, Svc::TlmSendConfig());
568
1/1
✓ Branch 6 taken 5 times.
10 this->cmdResponse_out(opCode, cmdSeq,
569
2/2
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
10 TlmPacketizer::toCmdResponse(this->overridePacket(section, packetId, requested)));
570 10 }
571
572 283 void TlmPacketizer::configureSectionGroupRate(
573 const Svc::TelemetrySection& section, //!< Section grouping
574 FwChanIdType tlmGroup, //!< Group Identifier
575 const Svc::RateLogic& rateLogic, //!< Rate Logic
576 U32 minDelta, //!< Minimum Sched Ticks to send packets on updates when using ON_CHANGE logic
577 U32 maxDelta //!< Maximum Sched Ticks between packets to send when using EVERY_MAX logic
578 ) {
579 283 FW_ASSERT(section.isValid());
580 283 FW_ASSERT(rateLogic.isValid());
581 // These two asserts are an "if" statement in a command so they will no assert on bad user data
582 283 FW_ASSERT(section >= 0 and section < TelemetrySection::NUM_SECTIONS);
583 283 FW_ASSERT(tlmGroup <= MAX_CONFIGURABLE_TLMPACKETIZER_GROUP);
584
585
2/2
✓ Branch 23 taken 283 times.
✓ Branch 27 taken 283 times.
283 this->m_groupConfigs[section][tlmGroup].set_rates(Svc::RateConfig(rateLogic, minDelta, maxDelta));
586
2/2
✓ Branch 6 taken 283 times.
✓ Branch 12 taken 283 times.
283 this->tlmWrite_GroupConfigs(this->m_groupConfigs);
587 283 }
588
589 646 FwIndexType TlmPacketizer::sectionGroupToPort(const FwIndexType section, const FwSizeType group) {
590 // Confirm the indices will not overflow the size of the array
591 646 FW_ASSERT(group < TlmPacketizer_TelemetrySendSection::SIZE, static_cast<FwAssertArgType>(group));
592 646 FW_ASSERT(section < TlmPacketizer_TelemetrySendPortMap::SIZE, static_cast<FwAssertArgType>(section));
593
594 646 const FwIndexType outIndex = TlmPacketizer::TELEMETRY_SEND_PORT_MAP[static_cast<FwSizeType>(section)][group];
595
596 // Confirm the output port index is within the valid number of telemetry send ports
597 646 FW_ASSERT(outIndex < TELEMETRY_SEND_PORTS, static_cast<FwAssertArgType>(outIndex));
598 646 return outIndex;
599 }
600
601 29 Fw::CmdResponse TlmPacketizer::toCmdResponse(PacketStatus status) {
602
3/3
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
29 switch (status) {
603 23 case PacketStatus::OK:
604 23 return Fw::CmdResponse::OK;
605 1 case PacketStatus::MIRROR_FAILED:
606 1 return Fw::CmdResponse::EXECUTION_ERROR;
607 5 default:
608 5 return Fw::CmdResponse::VALIDATION_ERROR;
609 }
610 }
611
612 337 Svc::TlmSendConfig TlmPacketizer::effectiveConfig(FwIndexType section, FwChanIdType pkt) const {
613 337 const FwSizeType sectionIndex = static_cast<FwSizeType>(section);
614 // Start from the group policy, then copy in the fields the packet overrides
615
3/3
✓ Branch 16 taken 337 times.
✓ Branch 22 taken 337 times.
✓ Branch 26 taken 337 times.
337 Svc::TlmSendConfig effective = this->m_groupConfigs[sectionIndex][this->m_fillBuffers[pkt].level];
616
2/2
✓ Branch 11 taken 337 times.
✓ Branch 17 taken 337 times.
337 const Svc::PacketOverrideConfig& packetOverride = this->m_packetOverrides[sectionIndex][pkt];
617
1/1
✓ Branch 6 taken 337 times.
337 const Svc::PacketOverrideType overrideType = packetOverride.get_overrideType();
618
3/4
✓ Branch 2 taken 319 times.
✓ Branch 3 taken 18 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 319 times.
656 const bool overridesSend = (overrideType == Svc::PacketOverrideType::OVERRIDE_SEND) or
619 319 (overrideType == Svc::PacketOverrideType::OVERRIDE_SEND_AND_RATES);
620
3/4
✓ Branch 2 taken 336 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 336 times.
673 const bool overridesRates = (overrideType == Svc::PacketOverrideType::OVERRIDE_RATES) or
621 336 (overrideType == Svc::PacketOverrideType::OVERRIDE_SEND_AND_RATES);
622 // A packet-level INHERIT_FROM_PARENT means "use the group's state", so it is not copied
623
6/6
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 319 times.
✓ Branch 10 taken 17 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 17 times.
✓ Branch 13 taken 320 times.
337 if (overridesSend and packetOverride.get_sendConfig().get_sendState() != Svc::TlmSendState::INHERIT_FROM_PARENT) {
624
1/1
✓ Branch 11 taken 17 times.
17 effective.set_sendState(packetOverride.get_sendConfig().get_sendState());
625 }
626
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 336 times.
337 if (overridesRates) {
627
1/1
✓ Branch 11 taken 1 times.
1 effective.set_rates(packetOverride.get_sendConfig().get_rates());
628 }
629 674 return effective;
630 337 }
631
632 331 bool TlmPacketizer::sendAllowed(const Svc::TlmSendState& sendState, FwIndexType section) const {
633
4/6
✗ Branch 3 not taken.
✓ Branch 4 taken 331 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 70 times.
✓ Branch 7 taken 258 times.
✗ Branch 8 not taken.
331 switch (sendState.e) {
634 3 case Svc::TlmSendState::SEND_ALWAYS:
635 3 return true;
636 70 case Svc::TlmSendState::SEND_NEVER:
637 70 return false;
638 // INHERIT_FROM_PARENT here is a group-level state, whose parent is the section. Fall-through intended.
639 258 case Svc::TlmSendState::SEND_WHEN_SECTION_ENABLED:
640 case Svc::TlmSendState::INHERIT_FROM_PARENT:
641 258 return this->m_sectionEnabled[static_cast<FwSizeType>(section)] == Fw::Enabled::ENABLED;
642 ✗ default:
643 ✗ FW_ASSERT(0, static_cast<FwAssertArgType>(sendState.e));
644 ✗ return false;
645 }
646 }
647
648 36 Fw::Success TlmPacketizer::findPacketIndexById(U32 packetId, FwChanIdType& pkt) const {
649
2/2
✓ Branch 4 taken 46 times.
✓ Branch 5 taken 5 times.
51 for (FwChanIdType p = 0; p < this->m_numPackets; p++) {
650
2/2
✓ Branch 5 taken 31 times.
✓ Branch 6 taken 15 times.
46 if (this->m_fillBuffers[p].id == packetId) {
651 31 pkt = p;
652 31 return Fw::Success::SUCCESS;
653 }
654 }
655 5 return Fw::Success::FAILURE;
656 }
657
658 33 TlmPacketizer::PacketStatus TlmPacketizer::resolvePacket(const Svc::TelemetrySection& section,
659 U32 packetId,
660 FwChanIdType& pkt) {
661
6/8
✓ Branch 4 taken 33 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 33 times.
✗ Branch 11 not taken.
✓ Branch 16 taken 2 times.
✓ Branch 17 taken 31 times.
✓ Branch 18 taken 2 times.
✓ Branch 19 taken 31 times.
33 if (not section.isValid() or section < 0 or section >= TelemetrySection::NUM_SECTIONS) {
662 2 this->log_WARNING_LO_InvalidPacketConfig(packetId);
663 2 return PacketStatus::INVALID_SECTION;
664 }
665
3/3
✓ Branch 2 taken 31 times.
✓ Branch 11 taken 4 times.
✓ Branch 12 taken 27 times.
31 if (this->findPacketIndexById(packetId, pkt) != Fw::Success::SUCCESS) {
666 4 this->log_WARNING_LO_PacketNotFound(packetId);
667 4 return PacketStatus::UNKNOWN_PACKET;
668 }
669 27 return PacketStatus::OK;
670 }
671
672 17 Svc::PacketOverrideType TlmPacketizer::mergeOverrideType(const Svc::PacketOverrideType& current,
673 const Svc::PacketOverrideType& requested) {
674
6/6
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 13 times.
✓ Branch 14 taken 3 times.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 16 times.
✓ Branch 17 taken 1 times.
17 if (current == Svc::PacketOverrideType::GROUP_DEFAULT or current == requested) {
675 16 return requested;
676 }
677 // Different fields already overridden: both apply
678 1 return Svc::PacketOverrideType::OVERRIDE_SEND_AND_RATES;
679 }
680
681 21 TlmPacketizer::PacketStatus TlmPacketizer::applyPacketOverride(const Svc::TelemetrySection& section,
682 FwChanIdType pkt,
683 const Svc::PacketOverrideConfig& requested) {
684
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
✓ Branch 11 taken 21 times.
✓ Branch 17 taken 21 times.
21 Svc::PacketOverrideConfig& stored = this->m_packetOverrides[static_cast<FwSizeType>(section.e)][pkt];
685 // Snapshot so a failed mirror can roll back and the persisted copy never diverges
686
1/1
✓ Branch 3 taken 21 times.
21 const Svc::PacketOverrideConfig previous = stored;
687 // Fields not (yet) overridden hold neutral values: INHERIT_FROM_PARENT and default rates
688 21 Svc::TlmSendConfig merged = (previous.get_overrideType() == Svc::PacketOverrideType::GROUP_DEFAULT)
689
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 8 times.
21 ? Svc::TlmSendConfig()
690
2/2
✓ Branch 1 taken 13 times.
✓ Branch 9 taken 8 times.
21 : stored.get_sendConfig();
691
3/4
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
21 switch (requested.get_overrideType()) {
692 14 case Svc::PacketOverrideType::OVERRIDE_SEND:
693
1/1
✓ Branch 10 taken 14 times.
14 merged.set_sendState(requested.get_sendConfig().get_sendState());
694 14 break;
695 3 case Svc::PacketOverrideType::OVERRIDE_RATES:
696
1/1
✓ Branch 10 taken 3 times.
3 merged.set_rates(requested.get_sendConfig().get_rates());
697 3 break;
698 ✗ case Svc::PacketOverrideType::OVERRIDE_SEND_AND_RATES:
699 ✗ merged = requested.get_sendConfig();
700 ✗ break;
701 4 default:
702 // GROUP_DEFAULT: clearing keeps no policy of its own
703
2/2
✓ Branch 2 taken 4 times.
✓ Branch 7 taken 4 times.
4 merged = Svc::TlmSendConfig();
704 4 break;
705 }
706
1/1
✓ Branch 4 taken 21 times.
21 stored.set_sendConfig(merged);
707
1/1
✓ Branch 8 taken 21 times.
42 stored.set_overrideType(
708 21 requested.get_overrideType() == Svc::PacketOverrideType::GROUP_DEFAULT
709
14/22
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 17 times.
✓ Branch 3 taken 4 times.
✓ Branch 8 taken 17 times.
✓ Branch 14 taken 17 times.
✓ Branch 18 taken 17 times.
✓ Branch 23 taken 17 times.
✓ Branch 24 taken 4 times.
✓ Branch 28 taken 17 times.
✓ Branch 29 taken 4 times.
✓ Branch 31 taken 17 times.
✓ Branch 32 taken 4 times.
✓ Branch 36 taken 17 times.
✓ Branch 37 taken 4 times.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
76 ? Svc::PacketOverrideType(Svc::PacketOverrideType::GROUP_DEFAULT)
710 34 : TlmPacketizer::mergeOverrideType(previous.get_overrideType(), requested.get_overrideType()));
711
3/3
✓ Branch 2 taken 21 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 20 times.
21 if (this->mirrorOverride(section, pkt) != Fw::Success::SUCCESS) {
712
1/1
✓ Branch 4 taken 1 times.
1 stored = previous;
713 1 return PacketStatus::MIRROR_FAILED;
714 }
715 20 return PacketStatus::OK;
716 21 }
717
718 24 TlmPacketizer::PacketStatus TlmPacketizer::overridePacket(const Svc::TelemetrySection& section,
719 U32 packetId,
720 const Svc::PacketOverrideConfig& requested) {
721 24 FwChanIdType pkt = 0;
722
1/1
✓ Branch 4 taken 24 times.
24 PacketStatus status = this->resolvePacket(section, packetId, pkt);
723
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 21 times.
24 if (status != PacketStatus::OK) {
724 3 return status;
725 }
726
1/1
✓ Branch 4 taken 21 times.
21 status = this->applyPacketOverride(section, pkt, requested);
727
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 20 times.
21 if (status != PacketStatus::OK) {
728 1 return status;
729 }
730
2/2
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 16 times.
20 if (requested.get_overrideType() == Svc::PacketOverrideType::GROUP_DEFAULT) {
731
1/1
✓ Branch 5 taken 4 times.
4 this->log_ACTIVITY_HI_PacketOverrideCleared(section, packetId);
732 } else {
733
1/1
✓ Branch 5 taken 16 times.
32 this->log_ACTIVITY_HI_PacketOverrideSet(section, packetId,
734
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
✓ Branch 11 taken 16 times.
✓ Branch 17 taken 16 times.
16 this->m_packetOverrides[static_cast<FwSizeType>(section.e)][pkt]);
735 }
736 20 return PacketStatus::OK;
737 }
738
739 6 void TlmPacketizer ::packetOverrideIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
740 // Reload per-packet overrides from the persistence component: one PacketConfigEntry record after another
741
2/2
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 1 times.
6 if (fwBuffer.isValid()) {
742
1/1
✓ Branch 2 taken 5 times.
5 auto deserializer = fwBuffer.getDeserializer();
743 5 const FwSizeType recordSize = static_cast<FwSizeType>(Svc::PacketConfigEntry::SERIALIZED_SIZE);
744
1/1
✓ Branch 4 taken 5 times.
5 const FwSizeType maxRecords = static_cast<FwSizeType>(fwBuffer.getSize()) / recordSize;
745 5 Fw::SerializeStatus status = Fw::FW_SERIALIZE_OK;
746
3/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
11 for (FwSizeType record = 0; (record < maxRecords) and (status == Fw::FW_SERIALIZE_OK); record++) {
747
1/1
✓ Branch 2 taken 7 times.
7 Svc::PacketConfigEntry entry;
748
1/1
✓ Branch 2 taken 7 times.
7 status = deserializer.deserializeTo(entry);
749
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (status != Fw::FW_SERIALIZE_OK) {
750 1 break;
751 }
752
1/1
✓ Branch 4 taken 6 times.
6 const Svc::TelemetrySection section = entry.get_section();
753 6 FwChanIdType pkt = 0;
754 // Enum values were validated by deserialization; only the section range remains to check
755
5/6
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 5 times.
6 if (section < 0 or section >= TelemetrySection::NUM_SECTIONS) {
756
1/1
✓ Branch 7 taken 1 times.
1 this->log_WARNING_LO_ReloadRecordInvalid(entry.get_packetId());
757
3/3
✓ Branch 4 taken 5 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 4 times.
5 } else if (this->findPacketIndexById(entry.get_packetId(), pkt) != Fw::Success::SUCCESS) {
758
1/1
✓ Branch 7 taken 1 times.
1 this->log_WARNING_LO_ReloadRecordUnknownPacket(entry.get_packetId());
759 } else {
760
4/5
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✓ Branch 13 taken 4 times.
✓ Branch 19 taken 4 times.
✓ Branch 25 taken 4 times.
4 this->m_packetOverrides[static_cast<FwSizeType>(section.e)][pkt] = entry.get_override();
761 }
762 7 }
763 // Trailing bytes that do not form a whole record are a malformed buffer
764
7/7
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✓ Branch 4 taken 4 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 3 times.
5 if ((status == Fw::FW_SERIALIZE_OK) and (deserializer.getDeserializeSizeLeft() != 0)) {
765 2 status = Fw::FW_DESERIALIZE_FORMAT_ERROR;
766 }
767
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if (status != Fw::FW_SERIALIZE_OK) {
768
1/1
✓ Branch 2 taken 3 times.
3 Fw::DeserialStatus reported(static_cast<Fw::DeserialStatus::T>(status));
769
2/3
✓ Branch 2 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 if (not reported.isValid()) {
770 ✗ reported = Fw::DeserialStatus::FORMAT_ERROR;
771 }
772
1/1
✓ Branch 5 taken 3 times.
3 this->log_WARNING_HI_ConfigReloadFailed(reported);
773 3 }
774 5 } else {
775
2/2
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->log_WARNING_HI_ConfigReloadFailed(Fw::DeserialStatus::BUFFER_EMPTY);
776 }
777 6 this->packetOverrideReturnOut_out(0, fwBuffer);
778 6 }
779
780 1 void TlmPacketizer ::packetOverrideReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
781 // Mirror buffers come from allocate and go back to the same pool; the SDD requires the whole
782 // mirror path (packetOverrideOut, allocate, deallocate, packetOverrideReturnIn) wired or none of it
783 1 FW_ASSERT(this->isConnected_deallocate_OutputPort(0));
784 1 this->deallocate_out(0, fwBuffer);
785 1 }
786
787 21 Fw::Success TlmPacketizer::mirrorOverride(const Svc::TelemetrySection& section, FwChanIdType pkt) {
788 // persistent storage component should mirror this state; skip if not wired
789
2/3
✓ Branch 5 taken 21 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 21 times.
21 if (not this->isConnected_packetOverrideOut_OutputPort(0)) {
790 ✗ return Fw::Success::SUCCESS;
791 }
792 // A mirror port without an allocator cannot persist anything, and without a deallocator the returned
793 // mirror buffers could never be freed; the SDD requires the whole mirror path wired or none of it
794 21 FW_ASSERT(this->isConnected_allocate_OutputPort(0));
795 21 FW_ASSERT(this->isConnected_deallocate_OutputPort(0));
796
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
21 const FwSizeType sectionIndex = static_cast<FwSizeType>(section.e);
797 42 const Svc::PacketConfigEntry entry(static_cast<U32>(this->m_fillBuffers[pkt].id), section,
798
3/3
✓ Branch 6 taken 21 times.
✓ Branch 12 taken 21 times.
✓ Branch 20 taken 21 times.
42 this->m_packetOverrides[sectionIndex][pkt]);
799 21 const FwSizeType entrySize = static_cast<FwSizeType>(Svc::PacketConfigEntry::SERIALIZED_SIZE);
800
1/1
✓ Branch 3 taken 21 times.
21 Fw::Buffer buffer = this->allocate_out(0, entrySize);
801
3/3
✓ Branch 2 taken 21 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 20 times.
21 if (not buffer.isValid()) {
802
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_ConfigMirrorAllocFailed();
803
1/1
✓ Branch 1 taken 1 times.
1 return Fw::Success::FAILURE;
804 }
805 // Fw.BufferGet contract: a valid buffer is at least the requested size
806 20 FW_ASSERT(static_cast<FwSizeType>(buffer.getSize()) >= entrySize, static_cast<FwAssertArgType>(buffer.getSize()));
807
1/1
✓ Branch 2 taken 20 times.
20 auto serializer = buffer.getSerializer();
808
1/1
✓ Branch 2 taken 20 times.
20 const Fw::SerializeStatus status = serializer.serializeFrom(entry);
809 20 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
810
1/1
✓ Branch 2 taken 20 times.
20 buffer.setSize(static_cast<Fw::Buffer::SizeType>(entrySize));
811
1/1
✓ Branch 5 taken 20 times.
20 this->packetOverrideOut_out(0, buffer);
812
1/1
✓ Branch 1 taken 20 times.
20 return Fw::Success::SUCCESS;
813 21 }
814
815 52 void TlmPacketizer::missingChannel(FwChanIdType id) {
816 // search to see if missing channel has already been sent
817
2/2
✓ Branch 0 taken 676 times.
✓ Branch 1 taken 1 times.
677 for (FwChanIdType slot = 0; slot < TLMPACKETIZER_MAX_MISSING_TLM_CHECK; slot++) {
818 // if it's been checked, return
819
5/6
✗ Branch 4 not taken.
✓ Branch 5 taken 676 times.
✓ Branch 6 taken 650 times.
✓ Branch 7 taken 26 times.
✓ Branch 13 taken 25 times.
✓ Branch 14 taken 625 times.
676 if (this->m_missTlmCheck[slot].checked and (this->m_missTlmCheck[slot].id == id)) {
820 25 return;
821
3/4
✗ Branch 4 not taken.
✓ Branch 5 taken 651 times.
✓ Branch 6 taken 26 times.
✓ Branch 7 taken 625 times.
651 } else if (not this->m_missTlmCheck[slot].checked) {
822 26 this->m_missTlmCheck[slot].checked = true;
823 26 this->m_missTlmCheck[slot].id = id;
824 26 this->log_WARNING_LO_NoChan(id);
825 26 return;
826 }
827 }
828 }
829
830 78 Fw::SerializeStatus TlmPacketizer::deserializeParam(const FwPrmIdType base_id,
831 const FwPrmIdType local_id,
832 const Fw::ParamValid prmStat,
833 Fw::SerialBufferBase& buff) {
834
4/6
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 72 times.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 78 times.
✗ Branch 11 not taken.
78 if (FW_PARAM_OK(prmStat)) {
835
2/3
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
78 switch (local_id) {
836 // A stored value of a different shape (e.g. saved by a previous FSW load) must not load as a scrambled
837 // table
838 38 case PARAMID_SECTION_ENABLED: {
839
1/1
✓ Branch 2 taken 38 times.
38 Svc::TlmPacketizer_SectionEnabled loaded;
840
1/1
✓ Branch 7 taken 38 times.
38 Fw::SerializeStatus status = buff.deserializeTo(loaded);
841
6/7
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 9 taken 38 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 37 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 37 times.
38 if ((status == Fw::FW_SERIALIZE_OK) and (buff.getDeserializeSizeLeft() != 0)) {
842 1 status = Fw::FW_DESERIALIZE_SIZE_MISMATCH;
843 }
844
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 1 times.
38 if (status == Fw::FW_SERIALIZE_OK) {
845
1/1
✓ Branch 6 taken 37 times.
37 this->m_sectionEnabled = loaded;
846 }
847 38 return status;
848 38 }
849 40 case PARAMID_SECTION_CONFIGS: {
850
1/1
✓ Branch 2 taken 40 times.
40 Svc::TlmPacketizer_SectionConfigs loaded;
851
1/1
✓ Branch 7 taken 40 times.
40 Fw::SerializeStatus status = buff.deserializeTo(loaded);
852
7/7
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 1 times.
✓ Branch 9 taken 39 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 38 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 39 times.
40 if ((status == Fw::FW_SERIALIZE_OK) and (buff.getDeserializeSizeLeft() != 0)) {
853 1 status = Fw::FW_DESERIALIZE_SIZE_MISMATCH;
854 }
855
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 2 times.
40 if (status == Fw::FW_SERIALIZE_OK) {
856
1/1
✓ Branch 6 taken 38 times.
38 this->m_groupConfigs = loaded;
857 }
858 40 return status;
859 40 }
860 ✗ default:
861 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(local_id));
862 }
863 }
864 ✗ return Fw::SerializeStatus::FW_DESERIALIZE_TYPE_MISMATCH;
865 }
866
867 2 Fw::SerializeStatus TlmPacketizer::serializeParam(const FwPrmIdType base_id,
868 const FwPrmIdType local_id,
869 Fw::SerialBufferBase& buff) const {
870
2/3
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 switch (local_id) {
871 1 case PARAMID_SECTION_ENABLED:
872 1 return buff.serializeFrom(this->m_sectionEnabled);
873 1 case PARAMID_SECTION_CONFIGS:
874 1 return buff.serializeFrom(this->m_groupConfigs);
875 ✗ default:
876 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(local_id));
877 }
878 ✗ return Fw::SerializeStatus::FW_SERIALIZE_FORMAT_ERROR;
879 }
880
881 } // end namespace Svc
882