GCC Code Coverage Report


Directory: /home/runner/work/fprime/fprime/Svc/TlmPacketizer/
File: TlmPacketizer.cpp
Date: 2026-11-03 22:12:02
Exec Total Coverage
Lines: 0 468 0.0%
Functions: 0 34 0.0%
Branches: 0 462 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 #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 ✗ TlmPacketizer ::TlmPacketizer(const char* const compName)
41 ✗ : TlmPacketizerComponentBase(compName), m_numPackets(0), m_configured(false), m_numChannels(0) {
42 // Register self as parameter delegate
43 ✗ this->registerExternalParameters(this);
44 // clear missing tlm channel check
45 ✗ for (FwChanIdType entry = 0; entry < TLMPACKETIZER_MAX_MISSING_TLM_CHECK; entry++) {
46 ✗ this->m_missTlmCheck[entry].checked = false;
47 ✗ this->m_missTlmCheck[entry].id = 0;
48 }
49
50 // clear packet buffers
51 ✗ for (FwChanIdType buffer = 0; buffer < MAX_PACKETIZER_PACKETS; buffer++) {
52 ✗ 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 ✗ }
58
59 ✗ TlmPacketizer ::~TlmPacketizer() {}
60
61 ✗ 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 ✗ FW_ASSERT(ignoreList.list || ignoreList.numEntries == 0);
69 ✗ FW_ASSERT(packetList.numEntries <= MAX_PACKETIZER_PACKETS, static_cast<FwAssertArgType>(packetList.numEntries));
70
71 // Reset key data members incase of reentrant calls
72 ✗ this->m_numChannels = 0;
73 ✗ this->m_channelIndices.clear();
74 ✗ this->m_configured = false;
75
76 // validate packet sizes against maximum com buffer size and populate hash
77 // table
78 ✗ FwChanIdType maxLevel = 0;
79 ✗ for (FwChanIdType pktEntry = 0; pktEntry < packetList.numEntries; pktEntry++) {
80 // Initial size is packetized telemetry descriptor + size of time tag + sizeof packet ID
81 ✗ 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 ✗ 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 ✗ for (FwChanIdType tlmEntry = 0; tlmEntry < packetList.list[pktEntry]->numEntries; tlmEntry++) {
88 ✗ FwChanIdType id = packetList.list[pktEntry]->list[tlmEntry].id;
89 ✗ const FwSizeType channelSize = packetList.list[pktEntry]->list[tlmEntry].size;
90 ✗ FwSizeType entryIndex = 0;
91 ✗ if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
92 // New channel - allocate a slot and initialize offsets to NOT_IN_PACKET
93 ✗ entryIndex = this->m_numChannels++;
94 ✗ this->m_channels[entryIndex].id = id;
95 ✗ this->m_channels[entryIndex].hasValue = false;
96 ✗ this->m_channels[entryIndex].channelSize = channelSize;
97 ✗ for (FwChanIdType pktOffsetEntry = 0; pktOffsetEntry < MAX_PACKETIZER_PACKETS; pktOffsetEntry++) {
98 ✗ this->m_channels[entryIndex].packetOffset[pktOffsetEntry] = NOT_IN_PACKET;
99 }
100 ✗ const Fw::Success insertStatus = this->m_channelIndices.insert(id, entryIndex);
101 ✗ FW_ASSERT(insertStatus == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(insertStatus));
102 ✗ } 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 ✗ 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 ✗ TlmEntry& entry = this->m_channels[entryIndex];
112 ✗ entry.ignored = false;
113 ✗ 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 ✗ FW_ASSERT(packetLen < NOT_IN_PACKET, static_cast<FwAssertArgType>(packetLen));
117 ✗ entry.packetOffset[pktEntry] = static_cast<FwTlmPacketizeOffsetType>(packetLen);
118
119 ✗ packetLen += entry.channelSize;
120
121 } // end channel in packet
122 ✗ FW_ASSERT(packetLen <= FW_COM_BUFFER_MAX_SIZE, static_cast<FwAssertArgType>(packetLen),
123 static_cast<FwAssertArgType>(pktEntry));
124 // clear contents
125 ✗ (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 ✗ Fw::SerializeStatus stat = this->m_fillBuffers[pktEntry].buffer.serializeFrom(
128 static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM));
129 ✗ FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
130 ✗ stat = this->m_fillBuffers[pktEntry].buffer.serializeFrom(packetList.list[pktEntry]->id);
131 ✗ FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
132 // set packet buffer length
133 ✗ stat = this->m_fillBuffers[pktEntry].buffer.setBuffLen(packetLen);
134 ✗ FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
135 // save ID
136 ✗ this->m_fillBuffers[pktEntry].id = packetList.list[pktEntry]->id;
137 // save level
138 ✗ this->m_fillBuffers[pktEntry].level = packetList.list[pktEntry]->level;
139 // store max level
140 ✗ if (packetList.list[pktEntry]->level > maxLevel) {
141 ✗ maxLevel = packetList.list[pktEntry]->level;
142 }
143
144 } // end packet list
145 ✗ 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 ✗ for (FwChanIdType channelEntry = 0; channelEntry < ignoreList.numEntries; channelEntry++) {
151 ✗ FwChanIdType id = ignoreList.list[channelEntry].id;
152 ✗ FwSizeType entryIndex = 0;
153 ✗ if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
154 // New channel - allocate a slot and initialize offsets to NOT_IN_PACKET
155 ✗ entryIndex = this->m_numChannels++;
156 ✗ this->m_channels[entryIndex].id = id;
157 ✗ this->m_channels[entryIndex].hasValue = false;
158 ✗ for (FwChanIdType pktOffsetEntry = 0; pktOffsetEntry < MAX_PACKETIZER_PACKETS; pktOffsetEntry++) {
159 ✗ this->m_channels[entryIndex].packetOffset[pktOffsetEntry] = NOT_IN_PACKET;
160 }
161 ✗ const Fw::Success insertStatus = this->m_channelIndices.insert(id, entryIndex);
162 ✗ FW_ASSERT(insertStatus == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(insertStatus));
163 ✗ } 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 ✗ TlmEntry& entry = this->m_channels[entryIndex];
169 ✗ entry.ignored = true;
170 ✗ entry.channelSize = ignoreList.list[channelEntry].size;
171 } // end ignore list
172
173 // store number of packets
174 ✗ this->m_numPackets = packetList.numEntries;
175
176 // indicate configured
177 ✗ this->m_configured = true;
178 ✗ }
179
180 // ----------------------------------------------------------------------
181 // Handler implementations for user-defined typed input ports
182 // ----------------------------------------------------------------------
183
184 ✗ void TlmPacketizer ::TlmRecv_handler(const FwIndexType portNum,
185 FwChanIdType id,
186 Fw::Time& timeTag,
187 Fw::TlmBuffer& val) {
188 ✗ FW_ASSERT(this->m_configured);
189 ✗ FwSizeType entryIndex = 0;
190
191 // Search to see if the channel is being tracked
192 ✗ if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
193 // channel not part of a packet and not ignored
194 ✗ this->missingChannel(id);
195 ✗ return;
196 }
197
198 ✗ TlmEntry& entry = this->m_channels[entryIndex];
199
200 // check to see if the channel is ignored. If so, just return.
201 ✗ if (entry.ignored) {
202 ✗ 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 ✗ if (val.getSize() > entry.channelSize) {
209 ✗ this->log_WARNING_HI_OversizedChannel(id, val.getSize(), entry.channelSize);
210 ✗ return;
211 }
212
213 // copy telemetry value into active buffers; hasValue written in-place via reference
214 ✗ for (FwChanIdType pkt = 0; pkt < MAX_PACKETIZER_PACKETS; pkt++) {
215 // check if current packet has this channel
216 ✗ if (entry.packetOffset[pkt] != NOT_IN_PACKET) {
217 // get destination address
218 ✗ Os::ScopeLock lock(this->m_lock);
219 ✗ this->m_fillBuffers[pkt].updated = true;
220 ✗ this->m_fillBuffers[pkt].latestTime = timeTag;
221 ✗ U8* ptr = &this->m_fillBuffers[pkt].buffer.getBuffAddr()[entry.packetOffset[pkt]];
222
223 ✗ (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 ✗ entry.hasValue = true;
226 ✗ }
227 }
228 }
229
230 ✗ 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 ✗ this->configureSectionGroupRate(section, tlmGroup, rateLogic, minDelta, maxDelta);
237 ✗ }
238
239 //! Handler for input port TlmGet
240 ✗ 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 ✗ FW_ASSERT(this->m_configured);
247 ✗ FwSizeType entryIndex = 0;
248
249 // Search to see if the channel is being tracked
250 ✗ if (this->m_channelIndices.find(id, entryIndex) != Fw::Success::SUCCESS) {
251 // channel not part of a packet and not ignored
252 ✗ this->missingChannel(id);
253 ✗ val.resetSer();
254 ✗ return Fw::TlmValid::INVALID;
255 }
256 ✗ 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 ✗ if (entry.ignored) {
261 ✗ val.resetSer();
262 ✗ return Fw::TlmValid::INVALID;
263 }
264
265 ✗ if (!entry.hasValue) {
266 // haven't received a value yet for this entry.
267 ✗ val.resetSer();
268 ✗ return Fw::TlmValid::INVALID;
269 }
270
271 // make sure we have enough space to store this entry in our buf
272 ✗ 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 ✗ for (FwChanIdType pkt = 0; pkt < MAX_PACKETIZER_PACKETS; pkt++) {
279 // check if current packet has this channel
280 ✗ if (entry.packetOffset[pkt] != NOT_IN_PACKET) {
281 // okay, it has the channel. copy chan val into the tlm buf
282 ✗ Os::ScopeLock lock(this->m_lock);
283 ✗ timeTag = this->m_fillBuffers[pkt].latestTime;
284 ✗ U8* ptr = &this->m_fillBuffers[pkt].buffer.getBuffAddr()[entry.packetOffset[pkt]];
285 ✗ (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 ✗ const Fw::SerializeStatus setStatus = val.setBuffLen(entry.channelSize);
289 ✗ FW_ASSERT(setStatus == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(setStatus));
290 ✗ return Fw::TlmValid::VALID;
291 ✗ }
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 ✗ void TlmPacketizer ::Run_handler(const FwIndexType portNum, U32 context) {
303 ✗ FW_ASSERT(this->m_configured);
304
305 ✗ for (FwChanIdType pkt = 0; pkt < this->m_numPackets; pkt++) {
306 // Local flags to track which sections require a packet dispatch
307 ✗ bool sectionNeedsSend[TelemetrySection::NUM_SECTIONS] = {false};
308 ✗ bool anySectionNeedsSend = false;
309
310 // Lock only to capture the update status and group, and reset the fill buffer flag.
311 ✗ bool isNewData = false;
312 ✗ FwChanIdType entryGroup = 0;
313 {
314 ✗ Os::ScopeLock lock(this->m_lock);
315 ✗ isNewData = this->m_fillBuffers[pkt].updated;
316 ✗ entryGroup = this->m_fillBuffers[pkt].level;
317 ✗ this->m_fillBuffers[pkt].updated = false;
318 ✗ }
319
320 ✗ for (FwIndexType section = 0; section < TelemetrySection::NUM_SECTIONS; section++) {
321 ✗ PktSendCounters& pktEntryFlags = this->m_packetFlags[static_cast<FwSizeType>(section)][pkt];
322 // Group policy with the packet's overridden fields copied in
323 ✗ const Svc::TlmSendConfig effectiveEntryConfig = this->effectiveConfig(section, pkt);
324
325 // Packet is updated and not REQUESTED (Keep REQUESTED marking to bypass disable checks)
326 ✗ if (isNewData && pktEntryFlags.updateFlag != UpdateFlag::REQUESTED) {
327 ✗ 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 ✗ if (!this->isConnected_PktSend_OutputPort(this->sectionGroupToPort(section, entryGroup))) {
339 ✗ continue;
340 }
341
342 ✗ if (pktEntryFlags.updateFlag == UpdateFlag::REQUESTED) {
343 ✗ sectionNeedsSend[section] = true;
344 } else {
345 ✗ if (not this->sendAllowed(effectiveEntryConfig.get_sendState(), section)) {
346 ✗ continue;
347 }
348 ✗ if (pktEntryFlags.updateFlag == UpdateFlag::NEVER_UPDATED) {
349 ✗ continue; // Avoid No Data
350 }
351 }
352
353 // Update Counter, prevent overflow.
354 ✗ if (pktEntryFlags.prevSentCounter < std::numeric_limits<U32>::max()) {
355 ✗ 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 ✗ if (pktEntryFlags.updateFlag == UpdateFlag::NEW and
364 ✗ effectiveEntryConfig.get_rates().get_rateLogic() != Svc::RateLogic::EVERY_MAX and
365 ✗ pktEntryFlags.prevSentCounter >= effectiveEntryConfig.get_rates().get_min()) {
366 ✗ sectionNeedsSend[section] = true;
367 }
368
369 /*
370 1. Group Logic includes checking MAX
371 2. Packet set counter is at MAX
372 */
373 ✗ if (effectiveEntryConfig.get_rates().get_rateLogic() != Svc::RateLogic::ON_CHANGE_MIN and
374 ✗ pktEntryFlags.prevSentCounter >= effectiveEntryConfig.get_rates().get_max()) {
375 ✗ sectionNeedsSend[section] = true;
376 }
377
378 ✗ if (sectionNeedsSend[section]) {
379 ✗ anySectionNeedsSend = true;
380 }
381 ✗ }
382
383 // Only perform the buffer copy if at least one section needs to send.
384 ✗ if (anySectionNeedsSend) {
385 // Snapshot only the packet data under the lock
386 ✗ Fw::ComBuffer sendBuffer;
387 ✗ Fw::Time sendTime;
388 {
389 ✗ Os::ScopeLock lock(this->m_lock);
390 ✗ sendBuffer = this->m_fillBuffers[pkt].buffer;
391 ✗ sendTime = this->m_fillBuffers[pkt].latestTime;
392 ✗ }
393
394 // serialize time into time offset in packet
395 Fw::ExternalSerializeBuffer buff(
396 ✗ &sendBuffer.getBuffAddr()[sizeof(FwPacketDescriptorType) + sizeof(FwTlmPacketizeIdType)],
397 ✗ Fw::Time::SERIALIZED_SIZE);
398 ✗ const Fw::SerializeStatus timeStatus = buff.serializeFrom(sendTime);
399 ✗ FW_ASSERT(Fw::FW_SERIALIZE_OK == timeStatus, static_cast<FwAssertArgType>(timeStatus));
400
401 ✗ for (FwIndexType section = 0; section < TelemetrySection::NUM_SECTIONS; section++) {
402 ✗ if (sectionNeedsSend[section]) {
403 ✗ PktSendCounters& pktEntryFlags = this->m_packetFlags[section][pkt];
404 ✗ FwIndexType outIndex = this->sectionGroupToPort(section, entryGroup);
405
406 ✗ this->PktSend_out(outIndex, sendBuffer, pktEntryFlags.prevSentCounter);
407
408 ✗ pktEntryFlags.prevSentCounter = 0;
409 ✗ pktEntryFlags.updateFlag = UpdateFlag::PAST;
410 }
411 }
412 ✗ }
413 }
414 ✗ }
415
416 ✗ 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 ✗ if (section.isValid() && section < TelemetrySection::NUM_SECTIONS && enabled.isValid()) {
422 ✗ (void)(this->m_sectionEnabled[static_cast<FwSizeType>(section)] = enabled);
423 } else {
424 ✗ this->log_WARNING_LO_SectionUnconfigurable(section, enabled);
425 }
426 ✗ }
427
428 ✗ void TlmPacketizer ::pingIn_handler(const FwIndexType portNum, U32 key) {
429 // return key
430 ✗ this->pingOut_out(0, key);
431 ✗ }
432
433 // ----------------------------------------------------------------------
434 // Command handler implementations
435 // ----------------------------------------------------------------------
436
437 ✗ void TlmPacketizer ::SET_LEVEL_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, FwChanIdType level) {
438 ✗ if (level > MAX_CONFIGURABLE_TLMPACKETIZER_GROUP) {
439 ✗ this->log_WARNING_LO_MaxLevelExceed(level, MAX_CONFIGURABLE_TLMPACKETIZER_GROUP);
440 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
441 ✗ return;
442 }
443 ✗ for (FwIndexType section = 0; section < TelemetrySection::NUM_SECTIONS; section++) {
444 ✗ for (FwChanIdType group = 0; group < NUM_CONFIGURABLE_TLMPACKETIZER_GROUPS; group++) {
445 ✗ 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 ✗ this->tlmWrite_GroupConfigs(this->m_groupConfigs);
450 ✗ this->log_ACTIVITY_HI_LevelSet(level);
451 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
452 }
453
454 ✗ void TlmPacketizer ::SEND_PKT_cmdHandler(const FwOpcodeType opCode,
455 const U32 cmdSeq,
456 const U32 id,
457 const Svc::TelemetrySection& section) {
458 ✗ FwChanIdType pkt = 0;
459 ✗ if (this->resolvePacket(section, id, pkt) != PacketStatus::OK) {
460 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
461 ✗ return;
462 }
463
464 {
465 ✗ Os::ScopeLock lock(this->m_lock);
466 ✗ this->m_fillBuffers[pkt].updated = true;
467 ✗ this->m_fillBuffers[pkt].latestTime = this->getTime();
468 ✗ }
469
470 ✗ this->m_packetFlags[section][pkt].updateFlag = UpdateFlag::REQUESTED;
471
472 ✗ this->log_ACTIVITY_HI_PacketSent(id);
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 ::CONFIGURE_GROUP_SEND_cmdHandler(FwOpcodeType opCode,
492 U32 cmdSeq,
493 const Svc::TelemetrySection& section,
494 FwChanIdType tlmGroup,
495 const Svc::TlmSendState& sendState) {
496 ✗ FW_ASSERT(section.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_sendState(sendState);
502 ✗ this->tlmWrite_GroupConfigs(this->m_groupConfigs);
503 ✗ this->log_ACTIVITY_HI_GroupSendStateSet(section, tlmGroup, sendState);
504 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
505 }
506
507 ✗ 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 ✗ FW_ASSERT(section.isValid());
515 ✗ FW_ASSERT(rateLogic.isValid());
516 ✗ 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 ✗ this->configureSectionGroupRate(section, tlmGroup, rateLogic, minDelta, maxDelta);
521 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
522 }
523
524 ✗ void TlmPacketizer ::GET_PACKET_CONFIG_cmdHandler(FwOpcodeType opCode,
525 U32 cmdSeq,
526 const Svc::TelemetrySection& section,
527 U32 packetId) {
528 ✗ FwChanIdType pkt = 0;
529 ✗ const PacketStatus status = this->resolvePacket(section, packetId, pkt);
530 ✗ if (status == PacketStatus::OK) {
531 ✗ const FwSizeType sectionIndex = static_cast<FwSizeType>(section.e);
532 ✗ this->log_ACTIVITY_HI_PacketConfigReport(section, packetId, this->m_packetOverrides[sectionIndex][pkt],
533 ✗ this->effectiveConfig(static_cast<FwIndexType>(section.e), pkt));
534 }
535 ✗ this->cmdResponse_out(opCode, cmdSeq, TlmPacketizer::toCmdResponse(status));
536 ✗ }
537
538 ✗ 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 const Svc::PacketOverrideConfig requested(Svc::PacketOverrideType::OVERRIDE_SEND,
544 ✗ Svc::TlmSendConfig(sendState, Svc::RateConfig()));
545 ✗ this->cmdResponse_out(opCode, cmdSeq,
546 ✗ TlmPacketizer::toCmdResponse(this->overridePacket(section, packetId, requested)));
547 ✗ }
548
549 ✗ 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 const Svc::PacketOverrideConfig requested(
557 Svc::PacketOverrideType::OVERRIDE_RATES,
558 ✗ Svc::TlmSendConfig(Svc::TlmSendState::INHERIT_FROM_PARENT, Svc::RateConfig(rateLogic, minDelta, maxDelta)));
559 ✗ this->cmdResponse_out(opCode, cmdSeq,
560 ✗ TlmPacketizer::toCmdResponse(this->overridePacket(section, packetId, requested)));
561 ✗ }
562
563 ✗ void TlmPacketizer ::CLEAR_PACKET_OVERRIDE_cmdHandler(FwOpcodeType opCode,
564 U32 cmdSeq,
565 const Svc::TelemetrySection& section,
566 U32 packetId) {
567 ✗ const Svc::PacketOverrideConfig requested(Svc::PacketOverrideType::GROUP_DEFAULT, Svc::TlmSendConfig());
568 ✗ this->cmdResponse_out(opCode, cmdSeq,
569 ✗ TlmPacketizer::toCmdResponse(this->overridePacket(section, packetId, requested)));
570 ✗ }
571
572 ✗ 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 ✗ FW_ASSERT(section.isValid());
580 ✗ 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 ✗ FW_ASSERT(section >= 0 and section < TelemetrySection::NUM_SECTIONS);
583 ✗ FW_ASSERT(tlmGroup <= MAX_CONFIGURABLE_TLMPACKETIZER_GROUP);
584
585 ✗ this->m_groupConfigs[section][tlmGroup].set_rates(Svc::RateConfig(rateLogic, minDelta, maxDelta));
586 ✗ this->tlmWrite_GroupConfigs(this->m_groupConfigs);
587 ✗ }
588
589 ✗ FwIndexType TlmPacketizer::sectionGroupToPort(const FwIndexType section, const FwSizeType group) {
590 // Confirm the indices will not overflow the size of the array
591 ✗ FW_ASSERT(group < TlmPacketizer_TelemetrySendSection::SIZE, static_cast<FwAssertArgType>(group));
592 ✗ FW_ASSERT(section < TlmPacketizer_TelemetrySendPortMap::SIZE, static_cast<FwAssertArgType>(section));
593
594 ✗ 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 ✗ FW_ASSERT(outIndex < TELEMETRY_SEND_PORTS, static_cast<FwAssertArgType>(outIndex));
598 ✗ return outIndex;
599 }
600
601 ✗ Fw::CmdResponse TlmPacketizer::toCmdResponse(PacketStatus status) {
602 ✗ switch (status) {
603 ✗ case PacketStatus::OK:
604 ✗ return Fw::CmdResponse::OK;
605 ✗ case PacketStatus::MIRROR_FAILED:
606 ✗ return Fw::CmdResponse::EXECUTION_ERROR;
607 ✗ default:
608 ✗ return Fw::CmdResponse::VALIDATION_ERROR;
609 }
610 }
611
612 ✗ Svc::TlmSendConfig TlmPacketizer::effectiveConfig(FwIndexType section, FwChanIdType pkt) const {
613 ✗ const FwSizeType sectionIndex = static_cast<FwSizeType>(section);
614 // Start from the group policy, then copy in the fields the packet overrides
615 ✗ Svc::TlmSendConfig effective = this->m_groupConfigs[sectionIndex][this->m_fillBuffers[pkt].level];
616 ✗ const Svc::PacketOverrideConfig& packetOverride = this->m_packetOverrides[sectionIndex][pkt];
617 ✗ const Svc::PacketOverrideType overrideType = packetOverride.get_overrideType();
618 ✗ const bool overridesSend = (overrideType == Svc::PacketOverrideType::OVERRIDE_SEND) or
619 ✗ (overrideType == Svc::PacketOverrideType::OVERRIDE_SEND_AND_RATES);
620 ✗ const bool overridesRates = (overrideType == Svc::PacketOverrideType::OVERRIDE_RATES) or
621 ✗ (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 ✗ if (overridesSend and packetOverride.get_sendConfig().get_sendState() != Svc::TlmSendState::INHERIT_FROM_PARENT) {
624 ✗ effective.set_sendState(packetOverride.get_sendConfig().get_sendState());
625 }
626 ✗ if (overridesRates) {
627 ✗ effective.set_rates(packetOverride.get_sendConfig().get_rates());
628 }
629 ✗ return effective;
630 ✗ }
631
632 ✗ bool TlmPacketizer::sendAllowed(const Svc::TlmSendState& sendState, FwIndexType section) const {
633 ✗ switch (sendState.e) {
634 ✗ case Svc::TlmSendState::SEND_ALWAYS:
635 ✗ return true;
636 ✗ case Svc::TlmSendState::SEND_NEVER:
637 ✗ return false;
638 // INHERIT_FROM_PARENT here is a group-level state, whose parent is the section. Fall-through intended.
639 ✗ case Svc::TlmSendState::SEND_WHEN_SECTION_ENABLED:
640 case Svc::TlmSendState::INHERIT_FROM_PARENT:
641 ✗ 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 ✗ Fw::Success TlmPacketizer::findPacketIndexById(U32 packetId, FwChanIdType& pkt) const {
649 ✗ for (FwChanIdType p = 0; p < this->m_numPackets; p++) {
650 ✗ if (this->m_fillBuffers[p].id == packetId) {
651 ✗ pkt = p;
652 ✗ return Fw::Success::SUCCESS;
653 }
654 }
655 ✗ return Fw::Success::FAILURE;
656 }
657
658 ✗ TlmPacketizer::PacketStatus TlmPacketizer::resolvePacket(const Svc::TelemetrySection& section,
659 U32 packetId,
660 FwChanIdType& pkt) {
661 ✗ if (not section.isValid() or section < 0 or section >= TelemetrySection::NUM_SECTIONS) {
662 ✗ this->log_WARNING_LO_InvalidPacketConfig(packetId);
663 ✗ return PacketStatus::INVALID_SECTION;
664 }
665 ✗ if (this->findPacketIndexById(packetId, pkt) != Fw::Success::SUCCESS) {
666 ✗ this->log_WARNING_LO_PacketNotFound(packetId);
667 ✗ return PacketStatus::UNKNOWN_PACKET;
668 }
669 ✗ return PacketStatus::OK;
670 }
671
672 ✗ Svc::PacketOverrideType TlmPacketizer::mergeOverrideType(const Svc::PacketOverrideType& current,
673 const Svc::PacketOverrideType& requested) {
674 ✗ if (current == Svc::PacketOverrideType::GROUP_DEFAULT or current == requested) {
675 ✗ return requested;
676 }
677 // Different fields already overridden: both apply
678 ✗ return Svc::PacketOverrideType::OVERRIDE_SEND_AND_RATES;
679 }
680
681 ✗ TlmPacketizer::PacketStatus TlmPacketizer::applyPacketOverride(const Svc::TelemetrySection& section,
682 FwChanIdType pkt,
683 const Svc::PacketOverrideConfig& requested) {
684 ✗ 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 ✗ const Svc::PacketOverrideConfig previous = stored;
687 // Fields not (yet) overridden hold neutral values: INHERIT_FROM_PARENT and default rates
688 ✗ Svc::TlmSendConfig merged = (previous.get_overrideType() == Svc::PacketOverrideType::GROUP_DEFAULT)
689 ✗ ? Svc::TlmSendConfig()
690 ✗ : stored.get_sendConfig();
691 ✗ switch (requested.get_overrideType()) {
692 ✗ case Svc::PacketOverrideType::OVERRIDE_SEND:
693 ✗ merged.set_sendState(requested.get_sendConfig().get_sendState());
694 ✗ break;
695 ✗ case Svc::PacketOverrideType::OVERRIDE_RATES:
696 ✗ merged.set_rates(requested.get_sendConfig().get_rates());
697 ✗ break;
698 ✗ case Svc::PacketOverrideType::OVERRIDE_SEND_AND_RATES:
699 ✗ merged = requested.get_sendConfig();
700 ✗ break;
701 ✗ default:
702 // GROUP_DEFAULT: clearing keeps no policy of its own
703 ✗ merged = Svc::TlmSendConfig();
704 ✗ break;
705 }
706 ✗ stored.set_sendConfig(merged);
707 ✗ stored.set_overrideType(
708 ✗ requested.get_overrideType() == Svc::PacketOverrideType::GROUP_DEFAULT
709 ✗ ? Svc::PacketOverrideType(Svc::PacketOverrideType::GROUP_DEFAULT)
710 : TlmPacketizer::mergeOverrideType(previous.get_overrideType(), requested.get_overrideType()));
711 ✗ if (this->mirrorOverride(section, pkt) != Fw::Success::SUCCESS) {
712 ✗ stored = previous;
713 ✗ return PacketStatus::MIRROR_FAILED;
714 }
715 ✗ return PacketStatus::OK;
716 ✗ }
717
718 ✗ TlmPacketizer::PacketStatus TlmPacketizer::overridePacket(const Svc::TelemetrySection& section,
719 U32 packetId,
720 const Svc::PacketOverrideConfig& requested) {
721 ✗ FwChanIdType pkt = 0;
722 ✗ PacketStatus status = this->resolvePacket(section, packetId, pkt);
723 ✗ if (status != PacketStatus::OK) {
724 ✗ return status;
725 }
726 ✗ status = this->applyPacketOverride(section, pkt, requested);
727 ✗ if (status != PacketStatus::OK) {
728 ✗ return status;
729 }
730 ✗ if (requested.get_overrideType() == Svc::PacketOverrideType::GROUP_DEFAULT) {
731 ✗ this->log_ACTIVITY_HI_PacketOverrideCleared(section, packetId);
732 } else {
733 ✗ this->log_ACTIVITY_HI_PacketOverrideSet(section, packetId,
734 ✗ this->m_packetOverrides[static_cast<FwSizeType>(section.e)][pkt]);
735 }
736 ✗ return PacketStatus::OK;
737 }
738
739 ✗ void TlmPacketizer ::packetOverrideIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
740 // Reload per-packet overrides from the persistence component: one PacketConfigEntry record after another
741 ✗ if (fwBuffer.isValid()) {
742 ✗ auto deserializer = fwBuffer.getDeserializer();
743 ✗ const FwSizeType recordSize = static_cast<FwSizeType>(Svc::PacketConfigEntry::SERIALIZED_SIZE);
744 ✗ const FwSizeType maxRecords = static_cast<FwSizeType>(fwBuffer.getSize()) / recordSize;
745 ✗ Fw::SerializeStatus status = Fw::FW_SERIALIZE_OK;
746 ✗ for (FwSizeType record = 0; (record < maxRecords) and (status == Fw::FW_SERIALIZE_OK); record++) {
747 ✗ Svc::PacketConfigEntry entry;
748 ✗ status = deserializer.deserializeTo(entry);
749 ✗ if (status != Fw::FW_SERIALIZE_OK) {
750 ✗ break;
751 }
752 ✗ const Svc::TelemetrySection section = entry.get_section();
753 ✗ FwChanIdType pkt = 0;
754 // Enum values were validated by deserialization; only the section range remains to check
755 ✗ if (section < 0 or section >= TelemetrySection::NUM_SECTIONS) {
756 ✗ this->log_WARNING_LO_ReloadRecordInvalid(entry.get_packetId());
757 ✗ } else if (this->findPacketIndexById(entry.get_packetId(), pkt) != Fw::Success::SUCCESS) {
758 ✗ this->log_WARNING_LO_ReloadRecordUnknownPacket(entry.get_packetId());
759 } else {
760 ✗ this->m_packetOverrides[static_cast<FwSizeType>(section.e)][pkt] = entry.get_override();
761 }
762 ✗ }
763 // Trailing bytes that do not form a whole record are a malformed buffer
764 ✗ if ((status == Fw::FW_SERIALIZE_OK) and (deserializer.getDeserializeSizeLeft() != 0)) {
765 ✗ status = Fw::FW_DESERIALIZE_FORMAT_ERROR;
766 }
767 ✗ if (status != Fw::FW_SERIALIZE_OK) {
768 ✗ Fw::DeserialStatus reported(static_cast<Fw::DeserialStatus::T>(status));
769 ✗ if (not reported.isValid()) {
770 ✗ reported = Fw::DeserialStatus::FORMAT_ERROR;
771 }
772 ✗ this->log_WARNING_HI_ConfigReloadFailed(reported);
773 ✗ }
774 ✗ } else {
775 ✗ this->log_WARNING_HI_ConfigReloadFailed(Fw::DeserialStatus::BUFFER_EMPTY);
776 }
777 ✗ this->packetOverrideReturnOut_out(0, fwBuffer);
778 ✗ }
779
780 ✗ 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 ✗ FW_ASSERT(this->isConnected_deallocate_OutputPort(0));
784 ✗ this->deallocate_out(0, fwBuffer);
785 ✗ }
786
787 ✗ Fw::Success TlmPacketizer::mirrorOverride(const Svc::TelemetrySection& section, FwChanIdType pkt) {
788 // persistent storage component should mirror this state; skip if not wired
789 ✗ 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 ✗ FW_ASSERT(this->isConnected_allocate_OutputPort(0));
795 ✗ FW_ASSERT(this->isConnected_deallocate_OutputPort(0));
796 ✗ const FwSizeType sectionIndex = static_cast<FwSizeType>(section.e);
797 ✗ const Svc::PacketConfigEntry entry(static_cast<U32>(this->m_fillBuffers[pkt].id), section,
798 ✗ this->m_packetOverrides[sectionIndex][pkt]);
799 ✗ const FwSizeType entrySize = static_cast<FwSizeType>(Svc::PacketConfigEntry::SERIALIZED_SIZE);
800 ✗ Fw::Buffer buffer = this->allocate_out(0, entrySize);
801 ✗ if (not buffer.isValid()) {
802 ✗ this->log_WARNING_HI_ConfigMirrorAllocFailed();
803 ✗ return Fw::Success::FAILURE;
804 }
805 // Fw.BufferGet contract: a valid buffer is at least the requested size
806 ✗ FW_ASSERT(static_cast<FwSizeType>(buffer.getSize()) >= entrySize, static_cast<FwAssertArgType>(buffer.getSize()));
807 ✗ auto serializer = buffer.getSerializer();
808 ✗ const Fw::SerializeStatus status = serializer.serializeFrom(entry);
809 ✗ FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status));
810 ✗ buffer.setSize(static_cast<Fw::Buffer::SizeType>(entrySize));
811 ✗ this->packetOverrideOut_out(0, buffer);
812 ✗ return Fw::Success::SUCCESS;
813 ✗ }
814
815 ✗ void TlmPacketizer::missingChannel(FwChanIdType id) {
816 // search to see if missing channel has already been sent
817 ✗ for (FwChanIdType slot = 0; slot < TLMPACKETIZER_MAX_MISSING_TLM_CHECK; slot++) {
818 // if it's been checked, return
819 ✗ if (this->m_missTlmCheck[slot].checked and (this->m_missTlmCheck[slot].id == id)) {
820 ✗ return;
821 ✗ } else if (not this->m_missTlmCheck[slot].checked) {
822 ✗ this->m_missTlmCheck[slot].checked = true;
823 ✗ this->m_missTlmCheck[slot].id = id;
824 ✗ this->log_WARNING_LO_NoChan(id);
825 ✗ return;
826 }
827 }
828 }
829
830 ✗ Fw::SerializeStatus TlmPacketizer::deserializeParam(const FwPrmIdType base_id,
831 const FwPrmIdType local_id,
832 const Fw::ParamValid prmStat,
833 Fw::SerialBufferBase& buff) {
834 ✗ if (FW_PARAM_OK(prmStat)) {
835 ✗ 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 ✗ case PARAMID_SECTION_ENABLED: {
839 ✗ Svc::TlmPacketizer_SectionEnabled loaded;
840 ✗ Fw::SerializeStatus status = buff.deserializeTo(loaded);
841 ✗ if ((status == Fw::FW_SERIALIZE_OK) and (buff.getDeserializeSizeLeft() != 0)) {
842 ✗ status = Fw::FW_DESERIALIZE_SIZE_MISMATCH;
843 }
844 ✗ if (status == Fw::FW_SERIALIZE_OK) {
845 ✗ this->m_sectionEnabled = loaded;
846 }
847 ✗ return status;
848 ✗ }
849 ✗ case PARAMID_SECTION_CONFIGS: {
850 ✗ Svc::TlmPacketizer_SectionConfigs loaded;
851 ✗ Fw::SerializeStatus status = buff.deserializeTo(loaded);
852 ✗ if ((status == Fw::FW_SERIALIZE_OK) and (buff.getDeserializeSizeLeft() != 0)) {
853 ✗ status = Fw::FW_DESERIALIZE_SIZE_MISMATCH;
854 }
855 ✗ if (status == Fw::FW_SERIALIZE_OK) {
856 ✗ this->m_groupConfigs = loaded;
857 }
858 ✗ return status;
859 ✗ }
860 ✗ default:
861 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(local_id));
862 }
863 }
864 ✗ return Fw::SerializeStatus::FW_DESERIALIZE_TYPE_MISMATCH;
865 }
866
867 ✗ Fw::SerializeStatus TlmPacketizer::serializeParam(const FwPrmIdType base_id,
868 const FwPrmIdType local_id,
869 Fw::SerialBufferBase& buff) const {
870 ✗ switch (local_id) {
871 ✗ case PARAMID_SECTION_ENABLED:
872 ✗ return buff.serializeFrom(this->m_sectionEnabled);
873 ✗ case PARAMID_SECTION_CONFIGS:
874 ✗ 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