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