GCC Code Coverage Report


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