GCC Code Coverage Report


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