F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
TlmPacketizer.cpp
Go to the documentation of this file.
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>
14 #include <cstring>
15 
16 namespace Svc {
17 
18 // ----------------------------------------------------------------------
19 // Construction, initialization, and destruction
20 // ----------------------------------------------------------------------
21 
22 TlmPacketizer ::TlmPacketizer(const char* const compName)
23  : TlmPacketizerComponentBase(compName), m_numPackets(0), m_configured(false), m_startLevel(0), m_maxLevel(0) {
24  // clear slot pointers
25  for (FwChanIdType entry = 0; entry < TLMPACKETIZER_NUM_TLM_HASH_SLOTS; entry++) {
26  this->m_tlmEntries.slots[entry] = nullptr;
27  }
28  // clear buckets
29  for (FwChanIdType entry = 0; entry < TLMPACKETIZER_HASH_BUCKETS; entry++) {
30  this->m_tlmEntries.buckets[entry].used = false;
31  this->m_tlmEntries.buckets[entry].bucketNo = entry;
32  this->m_tlmEntries.buckets[entry].next = nullptr;
33  this->m_tlmEntries.buckets[entry].id = 0;
34  }
35  // clear free index
36  this->m_tlmEntries.free = 0;
37  // clear missing tlm channel check
38  for (FwChanIdType entry = 0; entry < TLMPACKETIZER_MAX_MISSING_TLM_CHECK; entry++) {
39  this->m_missTlmCheck[entry].checked = false;
40  this->m_missTlmCheck[entry].id = 0;
41  }
42 
43  // clear packet buffers
44  for (FwChanIdType buffer = 0; buffer < MAX_PACKETIZER_PACKETS; buffer++) {
45  this->m_fillBuffers[buffer].updated = false;
46  this->m_fillBuffers[buffer].requested = false;
47  this->m_sendBuffers[buffer].updated = false;
48  }
49 }
50 
52 
54  const Svc::TlmPacketizerPacket& ignoreList,
55  const FwChanIdType startLevel) {
56  FW_ASSERT(packetList.list);
57  FW_ASSERT(ignoreList.list);
58  FW_ASSERT(packetList.numEntries <= MAX_PACKETIZER_PACKETS, static_cast<FwAssertArgType>(packetList.numEntries));
59  // validate packet sizes against maximum com buffer size and populate hash
60  // table
61  for (FwChanIdType pktEntry = 0; pktEntry < packetList.numEntries; pktEntry++) {
62  // Initial size is packetized telemetry descriptor + size of time tag + sizeof packet ID
63  FwSizeType packetLen =
65  FW_ASSERT(packetList.list[pktEntry]->list, static_cast<FwAssertArgType>(pktEntry));
66  // add up entries for each defined packet
67  for (FwChanIdType tlmEntry = 0; tlmEntry < packetList.list[pktEntry]->numEntries; tlmEntry++) {
68  // get hash value for id
69  FwChanIdType id = packetList.list[pktEntry]->list[tlmEntry].id;
70  TlmEntry* entryToUse = this->findBucket(id);
71  // copy into entry
72  FW_ASSERT(entryToUse);
73  entryToUse->used = true;
74  // not ignored channel
75  entryToUse->ignored = false;
76  entryToUse->id = id;
77  entryToUse->hasValue = false;
78  entryToUse->channelSize = packetList.list[pktEntry]->list[tlmEntry].size;
79  // the offset into the buffer will be the current packet length
80  // the offset must fit within FwSignedSizeType to allow for negative values
81  FW_ASSERT(packetLen <= static_cast<FwSizeType>(std::numeric_limits<FwSignedSizeType>::max()),
82  static_cast<FwAssertArgType>(packetLen));
83  entryToUse->packetOffset[pktEntry] = static_cast<FwSignedSizeType>(packetLen);
84 
85  packetLen += entryToUse->channelSize;
86 
87  } // end channel in packet
88  FW_ASSERT(packetLen <= FW_COM_BUFFER_MAX_SIZE, static_cast<FwAssertArgType>(packetLen),
89  static_cast<FwAssertArgType>(pktEntry));
90  // clear contents
91  memset(this->m_fillBuffers[pktEntry].buffer.getBuffAddr(), 0, static_cast<size_t>(packetLen));
92  // serialize packet descriptor and packet ID now since it will always be the same
93  Fw::SerializeStatus stat = this->m_fillBuffers[pktEntry].buffer.serializeFrom(
94  static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_PACKETIZED_TLM));
95  FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
96  stat = this->m_fillBuffers[pktEntry].buffer.serializeFrom(packetList.list[pktEntry]->id);
97  FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
98  // set packet buffer length
99  stat = this->m_fillBuffers[pktEntry].buffer.setBuffLen(packetLen);
100  FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
101  // save ID
102  this->m_fillBuffers[pktEntry].id = packetList.list[pktEntry]->id;
103  // save level
104  this->m_fillBuffers[pktEntry].level = packetList.list[pktEntry]->level;
105  // store max level
106  if (packetList.list[pktEntry]->level > this->m_maxLevel) {
107  this->m_maxLevel = packetList.list[pktEntry]->level;
108  }
109  // save start level
110  this->m_startLevel = startLevel;
111 
112  } // end packet list
113 
114  // populate hash table with ignore list
115  for (FwChanIdType channelEntry = 0; channelEntry < ignoreList.numEntries; channelEntry++) {
116  // get hash value for id
117  FwChanIdType id = ignoreList.list[channelEntry].id;
118 
119  TlmEntry* entryToUse = this->findBucket(id);
120 
121  // copy into entry
122  FW_ASSERT(entryToUse);
123  entryToUse->used = true;
124  // is ignored channel
125  entryToUse->ignored = true;
126  entryToUse->id = id;
127  entryToUse->hasValue = false;
128  entryToUse->channelSize = ignoreList.list[channelEntry].size;
129  } // end ignore list
130 
131  // store number of packets
132  this->m_numPackets = packetList.numEntries;
133 
134  // indicate configured
135  this->m_configured = true;
136 }
137 
138 TlmPacketizer::TlmEntry* TlmPacketizer::findBucket(FwChanIdType id) {
139  FwChanIdType index = this->doHash(id);
141  TlmEntry* entryToUse = nullptr;
142  TlmEntry* prevEntry = nullptr;
143 
144  // Search to see if channel has already been stored or a bucket needs to be added
145  if (this->m_tlmEntries.slots[index]) {
146  entryToUse = this->m_tlmEntries.slots[index];
147  for (FwChanIdType bucket = 0; bucket < TLMPACKETIZER_HASH_BUCKETS; bucket++) {
148  if (entryToUse) {
149  if (entryToUse->id == id) { // found the matching entry
150  break;
151  } else { // try next entry
152  prevEntry = entryToUse;
153  entryToUse = entryToUse->next;
154  }
155  } else {
156  // Make sure that we haven't run out of buckets
157  FW_ASSERT(this->m_tlmEntries.free < TLMPACKETIZER_HASH_BUCKETS,
158  static_cast<FwAssertArgType>(this->m_tlmEntries.free));
159  // add new bucket from free list
160  entryToUse = &this->m_tlmEntries.buckets[this->m_tlmEntries.free++];
161  // Coverity warning about null dereference - see if it happens
162  FW_ASSERT(prevEntry);
163  prevEntry->next = entryToUse;
164  // clear next pointer
165  entryToUse->next = nullptr;
166  // set all packet offsets to -1 for new entry
167  for (FwChanIdType pktOffsetEntry = 0; pktOffsetEntry < MAX_PACKETIZER_PACKETS; pktOffsetEntry++) {
168  entryToUse->packetOffset[pktOffsetEntry] = -1;
169  }
170  break;
171  }
172  }
173  } else {
174  // Make sure that we haven't run out of buckets
175  FW_ASSERT(this->m_tlmEntries.free < TLMPACKETIZER_HASH_BUCKETS,
176  static_cast<FwAssertArgType>(this->m_tlmEntries.free));
177  // create new entry at slot head
178  this->m_tlmEntries.slots[index] = &this->m_tlmEntries.buckets[this->m_tlmEntries.free++];
179  entryToUse = this->m_tlmEntries.slots[index];
180  entryToUse->next = nullptr;
181  // set all packet offsets to -1 for new entry
182  for (FwChanIdType pktOffsetEntry = 0; pktOffsetEntry < MAX_PACKETIZER_PACKETS; pktOffsetEntry++) {
183  entryToUse->packetOffset[pktOffsetEntry] = -1;
184  }
185  }
186 
187  return entryToUse;
188 }
189 
190 // ----------------------------------------------------------------------
191 // Handler implementations for user-defined typed input ports
192 // ----------------------------------------------------------------------
193 
194 void TlmPacketizer ::TlmRecv_handler(const FwIndexType portNum,
195  FwChanIdType id,
196  Fw::Time& timeTag,
197  Fw::TlmBuffer& val) {
198  FW_ASSERT(this->m_configured);
199  // get hash value for id
200  FwChanIdType index = this->doHash(id);
201  TlmEntry* entryToUse = nullptr;
202 
203  // Search to see if the channel is being sent
204  entryToUse = this->m_tlmEntries.slots[index];
205 
206  // if no entries at hash, channel not part of a packet or is not ignored
207  if (not entryToUse) {
208  this->missingChannel(id);
209  return;
210  }
211 
212  for (FwChanIdType bucket = 0; bucket < TLMPACKETIZER_HASH_BUCKETS; bucket++) {
213  if (entryToUse) {
214  if (entryToUse->id == id) { // found the matching entry
215  // check to see if the channel is ignored. If so, just return.
216  if (entryToUse->ignored) {
217  return;
218  }
219  break;
220  } else { // try next entry
221  entryToUse = entryToUse->next;
222  }
223  } else {
224  // telemetry channel not in any packets
225  this->missingChannel(id);
226  return;
227  }
228  }
229 
230  // copy telemetry value into active buffers
231  for (FwChanIdType pkt = 0; pkt < MAX_PACKETIZER_PACKETS; pkt++) {
232  // check if current packet has this channel
233  if (entryToUse->packetOffset[pkt] != -1) {
234  // get destination address
235  this->m_lock.lock();
236  this->m_fillBuffers[pkt].updated = true;
237  this->m_fillBuffers[pkt].latestTime = timeTag;
238  U8* ptr = &this->m_fillBuffers[pkt].buffer.getBuffAddr()[entryToUse->packetOffset[pkt]];
239  (void)memcpy(ptr, val.getBuffAddr(), static_cast<size_t>(val.getSize()));
240  // record that this chan has a value. could do this outside of the loop only once
241  // but then we'd need to grab the lock again.
242  entryToUse->hasValue = true;
243  this->m_lock.unLock();
244  }
245  }
246 }
247 
249 Fw::TlmValid TlmPacketizer ::TlmGet_handler(FwIndexType portNum,
250  FwChanIdType id,
251  Fw::Time& timeTag,
252  Fw::TlmBuffer& val
253 ) {
255  FW_ASSERT(this->m_configured);
256  // get hash value for id
257  FwChanIdType index = this->doHash(id);
258  TlmEntry* entryToUse = nullptr;
259 
260  // Search to see if the channel is being sent
261  entryToUse = this->m_tlmEntries.slots[index];
262 
263  // if no entries at hash, channel not part of a packet or is not ignored
264  if (not entryToUse) {
265  this->missingChannel(id);
266  val.resetSer();
267  return Fw::TlmValid::INVALID;
268  }
269 
270  for (FwChanIdType bucket = 0; bucket < TLMPACKETIZER_HASH_BUCKETS; bucket++) {
271  if (entryToUse) {
272  if (entryToUse->id == id) { // found the matching entry
273  // check to see if the channel is ignored. If so, just return, as
274  // we don't store the bytes of ignored channels
275  if (entryToUse->ignored) {
276  val.resetSer();
277  return Fw::TlmValid::INVALID;
278  }
279  break;
280  } else { // try next entry
281  entryToUse = entryToUse->next;
282  }
283  } else {
284  // telemetry channel not in any packets
285  this->missingChannel(id);
286  val.resetSer();
287  return Fw::TlmValid::INVALID;
288  }
289  }
290 
291  if (!entryToUse->hasValue) {
292  // haven't received a value yet for this entry.
293  val.resetSer();
294  return Fw::TlmValid::INVALID;
295  }
296 
297  // make sure we have enough space to store this entry in our buf
298  FW_ASSERT(entryToUse->channelSize <= val.getCapacity(), static_cast<FwAssertArgType>(entryToUse->channelSize),
299  static_cast<FwAssertArgType>(val.getCapacity()));
300 
301  // okay, we have the matching entry.
302  // go over each packet and find the first one which stores this channel
303 
304  for (FwChanIdType pkt = 0; pkt < MAX_PACKETIZER_PACKETS; pkt++) {
305  // check if current packet has this channel
306  if (entryToUse->packetOffset[pkt] != -1) {
307  // okay, it has the channel. copy chan val into the tlm buf
308  this->m_lock.lock();
309  timeTag = this->m_fillBuffers[pkt].latestTime;
310  U8* ptr = &this->m_fillBuffers[pkt].buffer.getBuffAddr()[entryToUse->packetOffset[pkt]];
311  (void)memcpy(val.getBuffAddr(), ptr, static_cast<size_t>(entryToUse->channelSize));
312  // set buf len to the channelSize. keep in mind, this is the MAX serialized size of the channel.
313  // so we may actually be filling val with some junk after the value of the channel.
314  FW_ASSERT(val.setBuffLen(entryToUse->channelSize) == Fw::SerializeStatus::FW_SERIALIZE_OK);
315  this->m_lock.unLock();
316  return Fw::TlmValid::VALID;
317  }
318  }
319 
320  // did not find a packet which stores this channel.
321  // coding error, this was not an ignored channel so it must be in a packet somewhere
322  FW_ASSERT(0, static_cast<FwAssertArgType>(entryToUse->id));
323  // TPP (tim paranoia principle)
324  val.resetSer();
325  return Fw::TlmValid::INVALID;
326 }
327 
328 void TlmPacketizer ::Run_handler(const FwIndexType portNum, U32 context) {
329  FW_ASSERT(this->m_configured);
330 
331  // Only write packets if connected
332  if (not this->isConnected_PktSend_OutputPort(0)) {
333  return;
334  }
335 
336  // lock mutex long enough to modify active telemetry buffer
337  // so the data can be read without worrying about updates
338  this->m_lock.lock();
339  // copy buffers from fill side to send side
340  for (FwChanIdType pkt = 0; pkt < this->m_numPackets; pkt++) {
341  if ((this->m_fillBuffers[pkt].updated) and
342  ((this->m_fillBuffers[pkt].level <= this->m_startLevel) or (this->m_fillBuffers[pkt].requested))) {
343  this->m_sendBuffers[pkt] = this->m_fillBuffers[pkt];
345  this->m_fillBuffers[pkt].updated = false;
346  }
347  this->m_fillBuffers[pkt].requested = false;
348  // PACKET_UPDATE_AFTER_FIRST_CHANGE will be this case - updated flag will not be cleared
349  } else if ((PACKET_UPDATE_ALWAYS == PACKET_UPDATE_MODE) and
350  (this->m_fillBuffers[pkt].level <= this->m_startLevel)) {
351  this->m_sendBuffers[pkt] = this->m_fillBuffers[pkt];
352  this->m_sendBuffers[pkt].updated = true;
353  } else {
354  this->m_sendBuffers[pkt].updated = false;
355  }
356  }
357  this->m_lock.unLock();
358 
359  // push all updated packet buffers
360  for (FwChanIdType pkt = 0; pkt < this->m_numPackets; pkt++) {
361  if (this->m_sendBuffers[pkt].updated) {
362  // serialize time into time offset in packet
364  &this->m_sendBuffers[pkt]
365  .buffer.getBuffAddr()[sizeof(FwPacketDescriptorType) + sizeof(FwTlmPacketizeIdType)],
367  Fw::SerializeStatus stat = buff.serializeFrom(this->m_sendBuffers[pkt].latestTime);
368  FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, stat);
369 
370  this->PktSend_out(0, this->m_sendBuffers[pkt].buffer, 0);
371  }
372  }
373 }
374 
375 void TlmPacketizer ::pingIn_handler(const FwIndexType portNum, U32 key) {
376  // return key
377  this->pingOut_out(0, key);
378 }
379 
380 // ----------------------------------------------------------------------
381 // Command handler implementations
382 // ----------------------------------------------------------------------
383 
384 void TlmPacketizer ::SET_LEVEL_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, FwChanIdType level) {
385  this->m_startLevel = level;
386  if (level > this->m_maxLevel) {
387  this->log_WARNING_LO_MaxLevelExceed(level, this->m_maxLevel);
388  }
389  this->tlmWrite_SendLevel(level);
390  this->log_ACTIVITY_HI_LevelSet(level);
391  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
392 }
393 
394 void TlmPacketizer ::SEND_PKT_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, U32 id) {
395  FwChanIdType pkt = 0;
396  for (pkt = 0; pkt < this->m_numPackets; pkt++) {
397  if (this->m_fillBuffers[pkt].id == id) {
398  this->m_lock.lock();
399  this->m_fillBuffers[pkt].updated = true;
400  this->m_fillBuffers[pkt].latestTime = this->getTime();
401  this->m_fillBuffers[pkt].requested = true;
402  this->m_lock.unLock();
403 
404  this->log_ACTIVITY_LO_PacketSent(id);
405  break;
406  }
407  }
408 
409  // couldn't find it
410  if (pkt == this->m_numPackets) {
412  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
413  return;
414  }
415 
416  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
417 }
418 
419 FwChanIdType TlmPacketizer::doHash(FwChanIdType id) {
421 }
422 
423 void TlmPacketizer::missingChannel(FwChanIdType id) {
424  // search to see if missing channel has already been sent
425  for (FwChanIdType slot = 0; slot < TLMPACKETIZER_MAX_MISSING_TLM_CHECK; slot++) {
426  // if it's been checked, return
427  if (this->m_missTlmCheck[slot].checked and (this->m_missTlmCheck[slot].id == id)) {
428  return;
429  } else if (not this->m_missTlmCheck[slot].checked) {
430  this->m_missTlmCheck[slot].checked = true;
431  this->m_missTlmCheck[slot].id = id;
432  this->log_WARNING_LO_NoChan(id);
433  return;
434  }
435  }
436 }
437 
438 } // end namespace Svc
Serialization/Deserialization operation was successful.
U16 FwPacketDescriptorType
The width of packet descriptors when they are serialized by the framework.
TlmPacketizer(const char *const compName)
FwIdType FwOpcodeType
The type of a command opcode.
SerializeStatus serializeFrom(U8 val, Endianness mode=Endianness::BIG) override
Serialize an 8-bit unsigned integer value.
FwSizeType size
serialized size of channel in bytes
PlatformSizeType FwSizeType
FwTlmPacketizeIdType id
packet ID
static const FwChanIdType TLMPACKETIZER_HASH_BUCKETS
Serializable::SizeType getSize() const override
Get current buffer size.
const TlmPacketizerPacket * list[MAX_PACKETIZER_PACKETS]
void log_WARNING_LO_NoChan(FwChanIdType Id) const
static const FwChanIdType MAX_PACKETIZER_PACKETS
void unLock()
unlock the mutex and assert success
Definition: Mutex.cpp:41
PlatformSignedSizeType FwSignedSizeType
void PktSend_out(FwIndexType portNum, Fw::ComBuffer &data, U32 context)
Invoke output port PktSend.
void cmdResponse_out(FwOpcodeType opCode, U32 cmdSeq, Fw::CmdResponse response)
Emit command response.
static const FwChanIdType TLMPACKETIZER_NUM_TLM_HASH_SLOTS
const TlmPacketizerChannelEntry * list
pointer to a channel entry
static const FwChanIdType TLMPACKETIZER_MAX_MISSING_TLM_CHECK
void tlmWrite_SendLevel(FwChanIdType arg, Fw::Time _tlmTime=Fw::Time()) const
SerializeStatus
forward declaration for string
FwChanIdType id
Id of channel.
FwChanIdType level
packet level - used to select set of packets to send
static const PacketUpdateMode PACKET_UPDATE_MODE
External serialize buffer with no copy semantics.
FwIdType FwChanIdType
The type of a telemetry channel identifier.
static const FwChanIdType TLMPACKETIZER_HASH_MOD_VALUE
U8 * getBuffAddr()
Get buffer address for data filling (non-const version)
Definition: TlmBuffer.cpp:42
void resetSer() override
Reset serialization pointer to beginning of buffer.
U16 FwTlmPacketizeIdType
The type of a telemetry packet identifier.
Command successfully executed.
void setPacketList(const TlmPacketizerPacketList &packetList, const Svc::TlmPacketizerPacket &ignoreList, const FwChanIdType startLevel)
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:53
void log_WARNING_LO_MaxLevelExceed(FwChanIdType level, FwChanIdType max) const
PlatformIndexType FwIndexType
FwSizeType getCapacity() const
Get buffer capacity.
Definition: TlmBuffer.cpp:30
void log_ACTIVITY_HI_LevelSet(FwChanIdType id) const
Command failed validation.
RateGroupDivider component implementation.
bool isConnected_PktSend_OutputPort(FwIndexType portNum)
SerializeStatus setBuffLen(Serializable::SizeType length) override
Set buffer length manually.
FwChanIdType numEntries
number of channels in packet
#define FW_ASSERT(...)
Definition: Assert.hpp:14
void pingOut_out(FwIndexType portNum, U32 key)
Invoke output port pingOut.
PlatformAssertArgType FwAssertArgType
The type of arguments to assert functions.
void lock()
lock the mutex and assert success
Definition: Mutex.cpp:34
Auto-generated base for TlmPacketizer component.