GCC Code Coverage Report


Directory: /home/runner/work/fprime/fprime/Svc/TlmPacketizer/
File: TlmPacketizer.cpp
Date: 2026-10-14 22:13:19
Exec Total Coverage
Lines: 0 298 0.0%
Functions: 0 20 0.0%
Branches: 0 279 0.0%

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
18 namespace Svc {
19
20 const TlmPacketizer_TelemetrySendPortMap TlmPacketizer::TELEMETRY_SEND_PORT_MAP = {};
21
22 static_assert(Svc::TelemetrySection::NUM_SECTIONS >= 1, "At least one telemetry section is required");
23
24 // ----------------------------------------------------------------------
25 // Construction, initialization, and destruction
26 // ----------------------------------------------------------------------
27
28 ✗ TlmPacketizer ::TlmPacketizer(const char* const compName)
29 ✗ : TlmPacketizerComponentBase(compName), m_numPackets(0), m_configured(false), m_numChannels(0) {
30 // Register self as parameter delegate
31 ✗ this->registerExternalParameters(this);
32 // clear missing tlm channel check
33 ✗ for (FwChanIdType entry = 0; entry < TLMPACKETIZER_MAX_MISSING_TLM_CHECK; entry++) {
34 ✗ this->m_missTlmCheck[entry].checked = false;
35 ✗ this->m_missTlmCheck[entry].id = 0;
36 }
37
38 // clear packet buffers
39 ✗ for (FwChanIdType buffer = 0; buffer < MAX_PACKETIZER_PACKETS; buffer++) {
40 ✗ this->m_fillBuffers[buffer].updated = false;
41 }
42
43 static_assert(NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS == MAX_CONFIGURABLE_TLMPACKETIZER_GROUP + 1,
44 "NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS MUST BE MAX_CONFIGURABLE_TLMPACKETIZER_GROUP + 1");
45 ✗ }
46
47 ✗ TlmPacketizer ::~TlmPacketizer() {}
48
49 ✗ void TlmPacketizer::setPacketList(const TlmPacketizerPacketList& packetList,
50 const Svc::TlmPacketizerPacket& ignoreList,
51 const FwChanIdType startLevel) {
52 // Ignore list may be nullptr as long as numEntries is 0. Providing an ignore list with numEntries 0 disables
53 // functionality for two reasons:
54 // 1. There are no ignored channels as configured by FPP.
55 // 2. Ignore functionality is intentionally disabled by project where nullptr was intentionally supplied.
56 ✗ FW_ASSERT(ignoreList.list || ignoreList.numEntries == 0);
57 ✗ FW_ASSERT(packetList.numEntries <= MAX_PACKETIZER_PACKETS, static_cast<FwAssertArgType>(packetList.numEntries));
58
59 // Reset key data members incase of reentrant calls
60 ✗ this->m_numChannels = 0;
61 ✗ this->m_channelIndices.clear();
62 ✗ this->m_configured = false;
63
64 // validate packet sizes against maximum com buffer size and populate hash
65 // table
66 ✗ FwChanIdType maxLevel = 0;
67 ✗ for (FwChanIdType pktEntry = 0; pktEntry < packetList.numEntries; pktEntry++) {
68 // Initial size is packetized telemetry descriptor + size of time tag + sizeof packet ID
69 ✗ FwSizeType packetLen =
70 sizeof(FwPacketDescriptorType) + Fw::Time::SERIALIZED_SIZE + sizeof(FwTlmPacketizeIdType);
71 // A packet with no channels is autocoded with a nullptr list and is permitted
72 ✗ FW_ASSERT(packetList.list[pktEntry]->list != nullptr || packetList.list[pktEntry]->numEntries == 0,
73 static_cast<FwAssertArgType>(pktEntry));
74 // add up entries for each defined packet
75 ✗ for (FwChanIdType tlmEntry = 0; tlmEntry < packetList.list[pktEntry]->numEntries; tlmEntry++) {
76 ✗ FwChanIdType id = packetList.list[pktEntry]->list[tlmEntry].id;
77 ✗ const FwSizeType channelSize = packetList.list[pktEntry]->list[tlmEntry].size;
78 ✗ FwSizeType entryIndex = 0;
79 ✗ if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
80 // New channel - allocate a slot and initialize offsets to NOT_IN_PACKET
81 ✗ entryIndex = this->m_numChannels++;
82 ✗ this->m_channels[entryIndex].id = id;
83 ✗ this->m_channels[entryIndex].hasValue = false;
84 ✗ this->m_channels[entryIndex].channelSize = channelSize;
85 ✗ for (FwChanIdType pktOffsetEntry = 0; pktOffsetEntry < MAX_PACKETIZER_PACKETS; pktOffsetEntry++) {
86 ✗ this->m_channels[entryIndex].packetOffset[pktOffsetEntry] = NOT_IN_PACKET;
87 }
88 ✗ const Fw::Success insertStatus = this->m_channelIndices.insert(id, entryIndex);
89 ✗ FW_ASSERT(insertStatus == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(insertStatus));
90 ✗ } else {
91 // Existing channel - a channel ID may repeat across packets, but its definition (size) must match.
92 // A conflicting size would corrupt the packet offsets computed from the earlier definition and
93 // overflow the fill buffer during TlmRecv/TlmGet copies, so reject the misconfiguration here.
94 ✗ FW_ASSERT(this->m_channels[entryIndex].channelSize == channelSize, static_cast<FwAssertArgType>(id),
95 static_cast<FwAssertArgType>(channelSize),
96 static_cast<FwAssertArgType>(this->m_channels[entryIndex].channelSize));
97 }
98 // not ignored channel - update entry in place via reference
99 ✗ TlmEntry& entry = this->m_channels[entryIndex];
100 ✗ entry.ignored = false;
101 ✗ entry.channelSize = channelSize;
102 // the offset into the buffer will be the current packet length
103 // the offset must fit within U16 and must not equal NOT_IN_PACKET
104 ✗ FW_ASSERT(packetLen < NOT_IN_PACKET, static_cast<FwAssertArgType>(packetLen));
105 ✗ entry.packetOffset[pktEntry] = static_cast<U16>(packetLen);
106
107 ✗ packetLen += entry.channelSize;
108
109 } // end channel in packet
110 ✗ FW_ASSERT(packetLen <= FW_COM_BUFFER_MAX_SIZE, static_cast<FwAssertArgType>(packetLen),
111 static_cast<FwAssertArgType>(pktEntry));
112 // clear contents
113 ✗ (void)memset(this->m_fillBuffers[pktEntry].buffer.getBuffAddr(), 0, static_cast<size_t>(packetLen));
114 // serialize packet descriptor and packet ID now since it will always be the same
115 ✗ Fw::SerializeStatus stat = this->m_fillBuffers[pktEntry].buffer.serializeFrom(
116 static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM));
117 ✗ FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
118 ✗ stat = this->m_fillBuffers[pktEntry].buffer.serializeFrom(packetList.list[pktEntry]->id);
119 ✗ FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
120 // set packet buffer length
121 ✗ stat = this->m_fillBuffers[pktEntry].buffer.setBuffLen(packetLen);
122 ✗ FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
123 // save ID
124 ✗ this->m_fillBuffers[pktEntry].id = packetList.list[pktEntry]->id;
125 // save level
126 ✗ this->m_fillBuffers[pktEntry].level = packetList.list[pktEntry]->level;
127 // store max level
128 ✗ if (packetList.list[pktEntry]->level > maxLevel) {
129 ✗ maxLevel = packetList.list[pktEntry]->level;
130 }
131
132 } // end packet list
133 ✗ FW_ASSERT(maxLevel <= MAX_CONFIGURABLE_TLMPACKETIZER_GROUP, static_cast<FwAssertArgType>(maxLevel));
134
135 // This section adds entries in the map for channels that are intended to be ignored. When the user supplies
136 // a list with no length, this loop is skipped. To turn-off ignoring of channels, the user can provide a null
137 // list with 0 length.
138 ✗ for (FwChanIdType channelEntry = 0; channelEntry < ignoreList.numEntries; channelEntry++) {
139 ✗ FwChanIdType id = ignoreList.list[channelEntry].id;
140 ✗ FwSizeType entryIndex = 0;
141 ✗ if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
142 // New channel - allocate a slot and initialize offsets to NOT_IN_PACKET
143 ✗ entryIndex = this->m_numChannels++;
144 ✗ this->m_channels[entryIndex].id = id;
145 ✗ this->m_channels[entryIndex].hasValue = false;
146 ✗ for (FwChanIdType pktOffsetEntry = 0; pktOffsetEntry < MAX_PACKETIZER_PACKETS; pktOffsetEntry++) {
147 ✗ this->m_channels[entryIndex].packetOffset[pktOffsetEntry] = NOT_IN_PACKET;
148 }
149 ✗ const Fw::Success insertStatus = this->m_channelIndices.insert(id, entryIndex);
150 ✗ FW_ASSERT(insertStatus == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(insertStatus));
151 ✗ } else {
152 // Ensure it is a duplicate in the ignore list, not a duplicate of a valid channel
153 ✗ FW_ASSERT(this->m_channels[entryIndex].ignored, static_cast<FwAssertArgType>(id));
154 }
155 // is ignored channel - update entry in place via reference
156 ✗ TlmEntry& entry = this->m_channels[entryIndex];
157 ✗ entry.ignored = true;
158 ✗ entry.channelSize = ignoreList.list[channelEntry].size;
159 } // end ignore list
160
161 // store number of packets
162 ✗ this->m_numPackets = packetList.numEntries;
163
164 // indicate configured
165 ✗ this->m_configured = true;
166 ✗ }
167
168 // ----------------------------------------------------------------------
169 // Handler implementations for user-defined typed input ports
170 // ----------------------------------------------------------------------
171
172 ✗ void TlmPacketizer ::TlmRecv_handler(const FwIndexType portNum,
173 FwChanIdType id,
174 Fw::Time& timeTag,
175 Fw::TlmBuffer& val) {
176 ✗ FW_ASSERT(this->m_configured);
177 ✗ FwSizeType entryIndex = 0;
178
179 // Search to see if the channel is being tracked
180 ✗ if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
181 // channel not part of a packet and not ignored
182 ✗ this->missingChannel(id);
183 ✗ return;
184 }
185
186 ✗ TlmEntry& entry = this->m_channels[entryIndex];
187
188 // check to see if the channel is ignored. If so, just return.
189 ✗ if (entry.ignored) {
190 ✗ return;
191 }
192
193 // An update larger than the configured channel size would overrun the packet
194 // fill buffer. Values may arrive from external sources (e.g. a hub bridging
195 // another address space), so reject rather than assert.
196 ✗ if (val.getSize() > entry.channelSize) {
197 ✗ this->log_WARNING_HI_OversizedChannel(id, val.getSize(), entry.channelSize);
198 ✗ return;
199 }
200
201 // copy telemetry value into active buffers; hasValue written in-place via reference
202 ✗ for (FwChanIdType pkt = 0; pkt < MAX_PACKETIZER_PACKETS; pkt++) {
203 // check if current packet has this channel
204 ✗ if (entry.packetOffset[pkt] != NOT_IN_PACKET) {
205 // get destination address
206 ✗ this->m_lock.lock();
207 ✗ this->m_fillBuffers[pkt].updated = true;
208 ✗ this->m_fillBuffers[pkt].latestTime = timeTag;
209 ✗ U8* ptr = &this->m_fillBuffers[pkt].buffer.getBuffAddr()[entry.packetOffset[pkt]];
210
211 ✗ (void)memcpy(ptr, val.getBuffAddr(), static_cast<size_t>(val.getSize()));
212 // set under the lock, after the copy, so TlmGet cannot see a value-less VALID entry
213 ✗ entry.hasValue = true;
214 ✗ this->m_lock.unLock();
215 }
216 }
217 }
218
219 ✗ void TlmPacketizer ::configureSectionGroupRate_handler(FwIndexType portNum,
220 const Svc::TelemetrySection& section,
221 FwChanIdType tlmGroup,
222 const Svc::RateLogic& rateLogic,
223 U32 minDelta,
224 U32 maxDelta) {
225 ✗ this->configureSectionGroupRate(section, tlmGroup, rateLogic, minDelta, maxDelta);
226 ✗ }
227
228 //! Handler for input port TlmGet
229 ✗ Fw::TlmValid TlmPacketizer ::TlmGet_handler(FwIndexType portNum, //!< The port number
230 FwChanIdType id, //!< Telemetry Channel ID
231 Fw::Time& timeTag, //!< Time Tag
232 Fw::TlmBuffer& val //!< Buffer containing serialized telemetry value.
233 //!< Size set to 0 if channel not found.
234 ) {
235 ✗ FW_ASSERT(this->m_configured);
236 ✗ FwSizeType entryIndex = 0;
237
238 // Search to see if the channel is being tracked
239 ✗ if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
240 // channel not part of a packet and not ignored
241 ✗ this->missingChannel(id);
242 ✗ val.resetSer();
243 ✗ return Fw::TlmValid::INVALID;
244 }
245 ✗ const TlmEntry& entry = this->m_channels[entryIndex];
246
247 // check to see if the channel is ignored. If so, just return, as
248 // we don't store the bytes of ignored channels
249 ✗ if (entry.ignored) {
250 ✗ val.resetSer();
251 ✗ return Fw::TlmValid::INVALID;
252 }
253
254 ✗ if (!entry.hasValue) {
255 // haven't received a value yet for this entry.
256 ✗ val.resetSer();
257 ✗ return Fw::TlmValid::INVALID;
258 }
259
260 // make sure we have enough space to store this entry in our buf
261 ✗ FW_ASSERT(entry.channelSize <= val.getCapacity(), static_cast<FwAssertArgType>(entry.channelSize),
262 static_cast<FwAssertArgType>(val.getCapacity()));
263
264 // okay, we have the matching entry.
265 // go over each packet and find the first one which stores this channel
266
267 ✗ for (FwChanIdType pkt = 0; pkt < MAX_PACKETIZER_PACKETS; pkt++) {
268 // check if current packet has this channel
269 ✗ if (entry.packetOffset[pkt] != NOT_IN_PACKET) {
270 // okay, it has the channel. copy chan val into the tlm buf
271 ✗ this->m_lock.lock();
272 ✗ timeTag = this->m_fillBuffers[pkt].latestTime;
273 ✗ U8* ptr = &this->m_fillBuffers[pkt].buffer.getBuffAddr()[entry.packetOffset[pkt]];
274 ✗ (void)memcpy(val.getBuffAddr(), ptr, static_cast<size_t>(entry.channelSize));
275 // set buf len to the channelSize. keep in mind, this is the MAX serialized size of the channel.
276 // so we may actually be filling val with some junk after the value of the channel.
277 ✗ const Fw::SerializeStatus setStatus = val.setBuffLen(entry.channelSize);
278 ✗ FW_ASSERT(setStatus == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(setStatus));
279 ✗ this->m_lock.unLock();
280 ✗ return Fw::TlmValid::VALID;
281 }
282 }
283
284 // did not find a packet which stores this channel.
285 // coding error, this was not an ignored channel so it must be in a packet somewhere
286 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(entry.id));
287 // TPP (tim paranoia principle)
288 ✗ val.resetSer();
289 ✗ return Fw::TlmValid::INVALID;
290 }
291
292 ✗ void TlmPacketizer ::Run_handler(const FwIndexType portNum, U32 context) {
293 ✗ FW_ASSERT(this->m_configured);
294
295 ✗ for (FwChanIdType pkt = 0; pkt < this->m_numPackets; pkt++) {
296 // Local flags to track which sections require a packet dispatch
297 ✗ bool sectionNeedsSend[TelemetrySection::NUM_SECTIONS] = {false};
298 ✗ bool anySectionNeedsSend = false;
299
300 // Lock only to capture the update status and reset the fill buffer flag.
301 ✗ this->m_lock.lock();
302 ✗ bool isNewData = this->m_fillBuffers[pkt].updated;
303 ✗ FwChanIdType entryGroup = this->m_fillBuffers[pkt].level;
304 ✗ this->m_fillBuffers[pkt].updated = false;
305 ✗ this->m_lock.unLock();
306
307 ✗ for (FwIndexType section = 0; section < TelemetrySection::NUM_SECTIONS; section++) {
308 ✗ PktSendCounters& pktEntryFlags = this->m_packetFlags[static_cast<FwSizeType>(section)][pkt];
309 TlmPacketizer_GroupConfig& entryGroupConfig =
310 ✗ this->m_groupConfigs[static_cast<FwSizeType>(section)][entryGroup];
311
312 // Packet is updated and not REQUESTED (Keep REQUESTED marking to bypass disable checks)
313 ✗ if (isNewData && pktEntryFlags.updateFlag != UpdateFlag::REQUESTED) {
314 ✗ pktEntryFlags.updateFlag = UpdateFlag::NEW;
315 }
316
317 /* Base conditions for sending
318 1. Output port is connected
319 2. The packet was requested (Override Checks).
320
321 If the packet wasn't requested:
322 3. The Section and Group in Section is enabled OR the Group in Section is force enabled
323 4. The rate logic is not SILENCED.
324 5. The packet has data (marked updated in the past or new)
325 */
326 ✗ if (!this->isConnected_PktSend_OutputPort(this->sectionGroupToPort(section, entryGroup))) {
327 ✗ continue;
328 }
329
330 ✗ if (pktEntryFlags.updateFlag == UpdateFlag::REQUESTED) {
331 ✗ sectionNeedsSend[section] = true;
332 } else {
333 ✗ if (not((entryGroupConfig.get_enabled() and
334 ✗ this->m_sectionEnabled[static_cast<FwSizeType>(section)] == Fw::Enabled::ENABLED) or
335 ✗ entryGroupConfig.get_forceEnabled() == Fw::Enabled::ENABLED)) {
336 ✗ continue;
337 }
338 ✗ if (entryGroupConfig.get_rateLogic() == Svc::RateLogic::SILENCED) {
339 ✗ continue;
340 }
341 ✗ if (pktEntryFlags.updateFlag == UpdateFlag::NEVER_UPDATED) {
342 ✗ continue; // Avoid No Data
343 }
344 }
345
346 // Update Counter, prevent overflow.
347 ✗ if (pktEntryFlags.prevSentCounter < std::numeric_limits<U32>::max()) {
348 ✗ pktEntryFlags.prevSentCounter++;
349 }
350
351 /*
352 1. Packet has been updated
353 2. Group Logic includes checking MIN
354 3. Packet sent counter at MIN
355 */
356 ✗ if (pktEntryFlags.updateFlag == UpdateFlag::NEW and
357 ✗ entryGroupConfig.get_rateLogic() != Svc::RateLogic::EVERY_MAX and
358 ✗ pktEntryFlags.prevSentCounter >= entryGroupConfig.get_min()) {
359 ✗ sectionNeedsSend[section] = true;
360 }
361
362 /*
363 1. Group Logic includes checking MAX
364 2. Packet set counter is at MAX
365 */
366 ✗ if (entryGroupConfig.get_rateLogic() != Svc::RateLogic::ON_CHANGE_MIN and
367 ✗ pktEntryFlags.prevSentCounter >= entryGroupConfig.get_max()) {
368 ✗ sectionNeedsSend[section] = true;
369 }
370
371 ✗ if (sectionNeedsSend[section]) {
372 ✗ anySectionNeedsSend = true;
373 }
374 }
375
376 // Only perform the buffer copy if at least one section needs to send.
377 ✗ if (anySectionNeedsSend) {
378 ✗ this->m_lock.lock();
379 ✗ BufferEntry sendBuffer = this->m_fillBuffers[pkt];
380 ✗ this->m_lock.unLock();
381
382 // serialize time into time offset in packet
383 Fw::ExternalSerializeBuffer buff(
384 ✗ &sendBuffer.buffer.getBuffAddr()[sizeof(FwPacketDescriptorType) + sizeof(FwTlmPacketizeIdType)],
385 ✗ Fw::Time::SERIALIZED_SIZE);
386 ✗ (void)buff.serializeFrom(sendBuffer.latestTime);
387
388 ✗ for (FwIndexType section = 0; section < TelemetrySection::NUM_SECTIONS; section++) {
389 ✗ if (sectionNeedsSend[section]) {
390 ✗ PktSendCounters& pktEntryFlags = this->m_packetFlags[section][pkt];
391 ✗ FwIndexType outIndex = this->sectionGroupToPort(section, entryGroup);
392
393 ✗ this->PktSend_out(outIndex, sendBuffer.buffer, pktEntryFlags.prevSentCounter);
394
395 ✗ pktEntryFlags.prevSentCounter = 0;
396 ✗ pktEntryFlags.updateFlag = UpdateFlag::PAST;
397 }
398 }
399 ✗ }
400 }
401 ✗ }
402
403 ✗ void TlmPacketizer ::controlIn_handler(FwIndexType portNum,
404 const Svc::TelemetrySection& section,
405 const Fw::Enabled& enabled) {
406 // NUM_SECTIONS is an enum constant (not a standalone constant), so isValid() accepts it.
407 // The explicit bounds check prevents an out-of-bounds write to m_sectionEnabled.
408 ✗ if (section.isValid() && section < TelemetrySection::NUM_SECTIONS && enabled.isValid()) {
409 ✗ (void)(this->m_sectionEnabled[static_cast<FwSizeType>(section)] = enabled);
410 } else {
411 ✗ this->log_WARNING_LO_SectionUnconfigurable(section, enabled);
412 }
413 ✗ }
414
415 ✗ void TlmPacketizer ::pingIn_handler(const FwIndexType portNum, U32 key) {
416 // return key
417 ✗ this->pingOut_out(0, key);
418 ✗ }
419
420 // ----------------------------------------------------------------------
421 // Command handler implementations
422 // ----------------------------------------------------------------------
423
424 ✗ void TlmPacketizer ::SET_LEVEL_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, FwChanIdType level) {
425 ✗ if (level > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) {
426 ✗ this->log_WARNING_LO_MaxLevelExceed(level, MAX_CONFIGURABLE_TLMPACKETIZER_GROUP);
427 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
428 ✗ return;
429 }
430 ✗ for (FwIndexType section = 0; section < TelemetrySection::NUM_SECTIONS; section++) {
431 ✗ for (FwChanIdType group = 0; group < NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS; group++) {
432 ✗ this->m_groupConfigs[static_cast<FwSizeType>(section)][group].set_enabled(
433 group <= level ? Fw::Enabled::ENABLED : Fw::Enabled::DISABLED);
434 }
435 }
436 ✗ this->tlmWrite_GroupConfigs(this->m_groupConfigs);
437 ✗ this->log_ACTIVITY_HI_LevelSet(level);
438 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
439 }
440
441 ✗ void TlmPacketizer ::SEND_PKT_cmdHandler(const FwOpcodeType opCode,
442 const U32 cmdSeq,
443 const U32 id,
444 const Svc::TelemetrySection& section) {
445 ✗ FW_ASSERT(section.isValid());
446 ✗ if (section < 0 or section >= TelemetrySection::NUM_SECTIONS) {
447 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
448 ✗ return;
449 }
450 ✗ FwChanIdType pkt = 0;
451 ✗ for (pkt = 0; pkt < this->m_numPackets; pkt++) {
452 ✗ if (this->m_fillBuffers[pkt].id == id) {
453 ✗ this->m_lock.lock();
454 ✗ this->m_fillBuffers[pkt].updated = true;
455 ✗ this->m_fillBuffers[pkt].latestTime = this->getTime();
456 ✗ this->m_lock.unLock();
457
458 ✗ this->m_packetFlags[section][pkt].updateFlag = UpdateFlag::REQUESTED;
459
460 ✗ this->log_ACTIVITY_LO_PacketSent(id);
461 ✗ break;
462 }
463 }
464
465 // couldn't find it
466 ✗ if (pkt == this->m_numPackets) {
467 ✗ log_WARNING_LO_PacketNotFound(id);
468 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
469 ✗ return;
470 }
471
472 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
473 }
474
475 ✗ void TlmPacketizer ::ENABLE_SECTION_cmdHandler(FwOpcodeType opCode,
476 U32 cmdSeq,
477 const Svc::TelemetrySection& section,
478 const Fw::Enabled& enable) {
479 ✗ FW_ASSERT(section.isValid());
480 ✗ FW_ASSERT(enable.isValid());
481 ✗ if (section < 0 or section >= TelemetrySection::NUM_SECTIONS) {
482 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
483 ✗ return;
484 }
485 ✗ (void)(this->m_sectionEnabled[section] = enable);
486 ✗ this->tlmWrite_SectionEnabled(this->m_sectionEnabled);
487 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
488 }
489
490 ✗ void TlmPacketizer ::ENABLE_GROUP_cmdHandler(FwOpcodeType opCode,
491 U32 cmdSeq,
492 const Svc::TelemetrySection& section,
493 FwChanIdType tlmGroup,
494 const Fw::Enabled& enable) {
495 ✗ FW_ASSERT(section.isValid());
496 ✗ FW_ASSERT(enable.isValid());
497 ✗ if (section < 0 or section >= TelemetrySection::NUM_SECTIONS or tlmGroup > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) {
498 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
499 ✗ return;
500 }
501 ✗ this->m_groupConfigs[section][tlmGroup].set_enabled(enable);
502 ✗ this->tlmWrite_GroupConfigs(this->m_groupConfigs);
503 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
504 }
505
506 ✗ void TlmPacketizer ::FORCE_GROUP_cmdHandler(FwOpcodeType opCode,
507 U32 cmdSeq,
508 const Svc::TelemetrySection& section,
509 FwChanIdType tlmGroup,
510 const Fw::Enabled& enable) {
511 ✗ FW_ASSERT(section.isValid());
512 ✗ FW_ASSERT(enable.isValid());
513 ✗ if (section < 0 or section >= TelemetrySection::NUM_SECTIONS or tlmGroup > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) {
514 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
515 ✗ return;
516 }
517 ✗ this->m_groupConfigs[section][tlmGroup].set_forceEnabled(enable);
518 ✗ this->tlmWrite_GroupConfigs(this->m_groupConfigs);
519 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
520 }
521
522 ✗ void TlmPacketizer ::CONFIGURE_GROUP_RATES_cmdHandler(FwOpcodeType opCode,
523 U32 cmdSeq,
524 const Svc::TelemetrySection& section,
525 FwChanIdType tlmGroup,
526 const Svc::RateLogic& rateLogic,
527 U32 minDelta,
528 U32 maxDelta) {
529 ✗ FW_ASSERT(section.isValid());
530 ✗ FW_ASSERT(rateLogic.isValid());
531 ✗ if (section < 0 or section >= TelemetrySection::NUM_SECTIONS or tlmGroup > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) {
532 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
533 ✗ return;
534 }
535 ✗ this->configureSectionGroupRate(section, tlmGroup, rateLogic, minDelta, maxDelta);
536 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
537 }
538
539 ✗ void TlmPacketizer::configureSectionGroupRate(
540 const Svc::TelemetrySection& section, //!< Section grouping
541 FwChanIdType tlmGroup, //!< Group Identifier
542 const Svc::RateLogic& rateLogic, //!< Rate Logic
543 U32 minDelta, //!< Minimum Sched Ticks to send packets on updates when using ON_CHANGE logic
544 U32 maxDelta //!< Maximum Sched Ticks between packets to send when using EVERY_MAX logic
545 ) {
546 ✗ FW_ASSERT(section.isValid());
547 ✗ FW_ASSERT(rateLogic.isValid());
548 // These two asserts are an "if" statement in a command so they will no assert on bad user data
549 ✗ FW_ASSERT(section >= 0 and section < TelemetrySection::NUM_SECTIONS);
550 ✗ FW_ASSERT(tlmGroup <= MAX_CONFIGURABLE_TLMPACKETIZER_GROUP);
551
552 ✗ TlmPacketizer_GroupConfig& groupConfig = this->m_groupConfigs[section][tlmGroup];
553 ✗ groupConfig.set_rateLogic(rateLogic);
554 ✗ groupConfig.set_min(minDelta);
555 ✗ groupConfig.set_max(maxDelta);
556 ✗ this->tlmWrite_GroupConfigs(this->m_groupConfigs);
557 ✗ }
558
559 ✗ FwIndexType TlmPacketizer::sectionGroupToPort(const FwIndexType section, const FwSizeType group) {
560 // Confirm the indices will not overflow the size of the array
561 ✗ FW_ASSERT(group < TlmPacketizer_TelemetrySendSection::SIZE, static_cast<FwAssertArgType>(group));
562 ✗ FW_ASSERT(section < TlmPacketizer_TelemetrySendPortMap::SIZE, static_cast<FwAssertArgType>(section));
563
564 ✗ const FwIndexType outIndex = TlmPacketizer::TELEMETRY_SEND_PORT_MAP[static_cast<FwSizeType>(section)][group];
565
566 // Confirm the output port index is within the valid number of telemetry send ports
567 ✗ FW_ASSERT(outIndex < TELEMETRY_SEND_PORTS, static_cast<FwAssertArgType>(outIndex));
568 ✗ return outIndex;
569 }
570
571 ✗ void TlmPacketizer::missingChannel(FwChanIdType id) {
572 // search to see if missing channel has already been sent
573 ✗ for (FwChanIdType slot = 0; slot < TLMPACKETIZER_MAX_MISSING_TLM_CHECK; slot++) {
574 // if it's been checked, return
575 ✗ if (this->m_missTlmCheck[slot].checked and (this->m_missTlmCheck[slot].id == id)) {
576 ✗ return;
577 ✗ } else if (not this->m_missTlmCheck[slot].checked) {
578 ✗ this->m_missTlmCheck[slot].checked = true;
579 ✗ this->m_missTlmCheck[slot].id = id;
580 ✗ this->log_WARNING_LO_NoChan(id);
581 ✗ return;
582 }
583 }
584 }
585
586 ✗ Fw::SerializeStatus TlmPacketizer::deserializeParam(const FwPrmIdType base_id,
587 const FwPrmIdType local_id,
588 const Fw::ParamValid prmStat,
589 Fw::SerialBufferBase& buff) {
590 ✗ if (FW_PARAM_OK(prmStat)) {
591 ✗ switch (local_id) {
592 ✗ case PARAMID_SECTION_ENABLED:
593 ✗ return buff.deserializeTo(this->m_sectionEnabled);
594 ✗ case PARAMID_SECTION_CONFIGS:
595 ✗ return buff.deserializeTo(this->m_groupConfigs);
596 ✗ default:
597 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(local_id));
598 }
599 }
600 ✗ return Fw::SerializeStatus::FW_DESERIALIZE_TYPE_MISMATCH;
601 }
602
603 ✗ Fw::SerializeStatus TlmPacketizer::serializeParam(const FwPrmIdType base_id,
604 const FwPrmIdType local_id,
605 Fw::SerialBufferBase& buff) const {
606 ✗ switch (local_id) {
607 ✗ case PARAMID_SECTION_ENABLED:
608 ✗ return buff.serializeFrom(this->m_sectionEnabled);
609 ✗ case PARAMID_SECTION_CONFIGS:
610 ✗ return buff.serializeFrom(this->m_groupConfigs);
611 ✗ default:
612 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(local_id));
613 }
614 ✗ return Fw::SerializeStatus::FW_SERIALIZE_FORMAT_ERROR;
615 }
616
617 } // end namespace Svc
618