F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
TlmChan.cpp
Go to the documentation of this file.
1 
12 #include <Fw/Com/ComBuffer.hpp>
13 #include <Fw/FPrimeBasicTypes.hpp>
14 #include <Fw/Types/Assert.hpp>
15 #include <Svc/TlmChan/TlmChan.hpp>
16 
17 namespace Svc {
18 
19 // Definition of TLMCHAN_HASH_BUCKETS is >= number of telemetry ids
20 static_assert(std::numeric_limits<FwChanIdType>::max() >= TLMCHAN_HASH_BUCKETS,
21  "Cannot have more hash buckets than maximum telemetry ids in the system");
22 // TLMCHAN_HASH_BUCKETS >= TLMCHAN_NUM_TLM_HASH_SLOTS >= 0
23 static_assert(std::numeric_limits<FwChanIdType>::max() >= TLMCHAN_NUM_TLM_HASH_SLOTS,
24  "Cannot have more hash slots than maximum telemetry ids in the system");
25 
26 TlmChan::TlmChan(const char* name) : TlmChanComponentBase(name), m_activeBuffer(0) {
27  // clear slot pointers
28  for (FwChanIdType entry = 0; entry < TLMCHAN_NUM_TLM_HASH_SLOTS; entry++) {
29  this->m_tlmEntries[0].slots[entry] = nullptr;
30  this->m_tlmEntries[1].slots[entry] = nullptr;
31  }
32  // clear buckets
33  for (FwChanIdType entry = 0; entry < TLMCHAN_HASH_BUCKETS; entry++) {
34  this->m_tlmEntries[0].buckets[entry].used = false;
35  this->m_tlmEntries[0].buckets[entry].updated = false;
36  this->m_tlmEntries[0].buckets[entry].bucketNo = entry;
37  this->m_tlmEntries[0].buckets[entry].next = nullptr;
38  this->m_tlmEntries[0].buckets[entry].id = 0;
39  this->m_tlmEntries[1].buckets[entry].used = false;
40  this->m_tlmEntries[1].buckets[entry].updated = false;
41  this->m_tlmEntries[1].buckets[entry].bucketNo = entry;
42  this->m_tlmEntries[1].buckets[entry].next = nullptr;
43  this->m_tlmEntries[1].buckets[entry].id = 0;
44  }
45  // clear free index
46  this->m_tlmEntries[0].free = 0;
47  this->m_tlmEntries[1].free = 0;
48 }
49 
51 
53  return (id % TLMCHAN_HASH_MOD_VALUE) % TLMCHAN_NUM_TLM_HASH_SLOTS;
54 }
55 
56 void TlmChan::pingIn_handler(const FwIndexType portNum, U32 key) {
57  // return key
58  this->pingOut_out(0, key);
59 }
60 
61 Fw::TlmValid TlmChan::TlmGet_handler(FwIndexType portNum, FwChanIdType id, Fw::Time& timeTag, Fw::TlmBuffer& val) {
62  // Compute index for entry
63 
64  FwChanIdType index = this->doHash(id);
65 
66  // Search to see if channel has been stored
67  // check both buffers
68  // don't need to lock because this port is guarded
69  TlmEntry* activeEntry = this->m_tlmEntries[this->m_activeBuffer].slots[index];
70  for (FwChanIdType bucket = 0; bucket < TLMCHAN_HASH_BUCKETS; bucket++) {
71  if (activeEntry) { // If bucket exists, check id
72  if (activeEntry->id == id) {
73  break;
74  } else { // otherwise go to next bucket
75  activeEntry = activeEntry->next;
76  }
77  } else { // no buckets left to search
78  break;
79  }
80  }
81 
82  TlmEntry* inactiveEntry = this->m_tlmEntries[1 - this->m_activeBuffer].slots[index];
83  for (FwChanIdType bucket = 0; bucket < TLMCHAN_HASH_BUCKETS; bucket++) {
84  if (inactiveEntry) { // If bucket exists, check id
85  if (inactiveEntry->id == id) {
86  break;
87  } else { // otherwise go to next bucket
88  inactiveEntry = inactiveEntry->next;
89  }
90  } else { // no buckets left to search
91  break;
92  }
93  }
94 
95  if (activeEntry && inactiveEntry) {
96  Fw::Time::Comparison cmp = Fw::Time::compare(inactiveEntry->lastUpdate, activeEntry->lastUpdate);
97  // two entries. grab the one with the most recent time tag
98  if (cmp == Fw::Time::Comparison::GT) {
99  // inactive entry is more recent
100  val = inactiveEntry->buffer;
101  timeTag = inactiveEntry->lastUpdate;
102  return Fw::TlmValid::VALID;
103  } else if (cmp != Fw::Time::Comparison::INCOMPARABLE) {
104  // active entry is more recent, or they are equal
105  val = activeEntry->buffer;
106  timeTag = activeEntry->lastUpdate;
107  return Fw::TlmValid::VALID;
108  } else {
109  // times are incomparable
110  // return the one that is updated, or if neither,
111  // default to active
112  if (inactiveEntry->updated) {
113  val = inactiveEntry->buffer;
114  timeTag = inactiveEntry->lastUpdate;
115  return Fw::TlmValid::VALID;
116  } else {
117  val = activeEntry->buffer;
118  timeTag = activeEntry->lastUpdate;
119  return Fw::TlmValid::VALID;
120  }
121  }
122  } else if (activeEntry) {
123  // only one entry, and it's in the active buf
124  val = activeEntry->buffer;
125  timeTag = activeEntry->lastUpdate;
126  return Fw::TlmValid::VALID;
127  } else if (inactiveEntry) {
128  // only one entry, and it's in the inactive buf
129  val = inactiveEntry->buffer;
130  timeTag = inactiveEntry->lastUpdate;
131  return Fw::TlmValid::VALID;
132  } else {
133  val.resetSer();
134  }
135  return Fw::TlmValid::INVALID;
136 }
137 
138 void TlmChan::TlmRecv_handler(FwIndexType portNum, FwChanIdType id, Fw::Time& timeTag, Fw::TlmBuffer& val) {
139  // Compute index for entry
140 
141  FwChanIdType index = this->doHash(id);
142  TlmEntry* entryToUse = nullptr;
143  TlmEntry* prevEntry = nullptr;
144 
145  // Search to see if channel has already been stored or a bucket needs to be added
146  if (this->m_tlmEntries[this->m_activeBuffer].slots[index]) {
147  entryToUse = this->m_tlmEntries[this->m_activeBuffer].slots[index];
148  // Loop one extra time so that we don't inadvertently fall through the end of the loop early.
149  for (FwChanIdType bucket = 0; bucket < TLMCHAN_HASH_BUCKETS + 1; bucket++) {
150  if (entryToUse) {
151  if (entryToUse->id == id) { // found the matching entry
152  break;
153  } else { // try next entry
154  prevEntry = entryToUse;
155  entryToUse = entryToUse->next;
156  }
157  } else {
158  // Make sure that we haven't run out of buckets
159  FW_ASSERT(this->m_tlmEntries[this->m_activeBuffer].free < TLMCHAN_HASH_BUCKETS);
160  // add new bucket from free list
161  entryToUse =
162  &this->m_tlmEntries[this->m_activeBuffer].buckets[this->m_tlmEntries[this->m_activeBuffer].free++];
163  FW_ASSERT(prevEntry);
164  prevEntry->next = entryToUse;
165  // clear next pointer
166  entryToUse->next = nullptr;
167  break;
168  }
169  }
170  } else {
171  // Make sure that we haven't run out of buckets
172  FW_ASSERT(this->m_tlmEntries[this->m_activeBuffer].free < TLMCHAN_HASH_BUCKETS);
173  // create new entry at slot head
174  this->m_tlmEntries[this->m_activeBuffer].slots[index] =
175  &this->m_tlmEntries[this->m_activeBuffer].buckets[this->m_tlmEntries[this->m_activeBuffer].free++];
176  entryToUse = this->m_tlmEntries[this->m_activeBuffer].slots[index];
177  entryToUse->next = nullptr;
178  }
179 
180  // copy into entry
181  FW_ASSERT(entryToUse);
182  entryToUse->used = true;
183  entryToUse->id = id;
184  entryToUse->updated = true;
185  entryToUse->lastUpdate = timeTag;
186  entryToUse->buffer = val;
187 }
188 
189 void TlmChan::Run_handler(FwIndexType portNum, U32 context) {
190  // Only write packets if connected
191  if (not this->isConnected_PktSend_OutputPort(0)) {
192  return;
193  }
194 
195  // lock mutex long enough to modify active telemetry buffer
196  // so the data can be read without worrying about updates
197  this->lock();
198  this->m_activeBuffer = 1 - this->m_activeBuffer;
199  // set activeBuffer to not updated
200  for (U32 entry = 0; entry < TLMCHAN_HASH_BUCKETS; entry++) {
201  this->m_tlmEntries[this->m_activeBuffer].buckets[entry].updated = false;
202  }
203  this->unLock();
204 
205  // go through each entry and send a packet if it has been updated
206  Fw::TlmPacket pkt;
207  pkt.resetPktSer();
208 
209  for (U32 entry = 0; entry < TLMCHAN_HASH_BUCKETS; entry++) {
210  TlmEntry* p_entry = &this->m_tlmEntries[1 - this->m_activeBuffer].buckets[entry];
211  if ((p_entry->updated) && (p_entry->used)) {
212  Fw::SerializeStatus stat = pkt.addValue(p_entry->id, p_entry->lastUpdate, p_entry->buffer);
213 
214  // check to see if this packet is full, if so, send it
215  if (Fw::FW_SERIALIZE_NO_ROOM_LEFT == stat) {
216  this->PktSend_out(0, pkt.getBuffer(), 0);
217  // reset packet for more entries
218  pkt.resetPktSer();
219  // add entry to new packet
220  stat = pkt.addValue(p_entry->id, p_entry->lastUpdate, p_entry->buffer);
221  // if this doesn't work, that means packet isn't big enough for
222  // even one channel, so assert
223  FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, static_cast<FwAssertArgType>(stat));
224  } else if (Fw::FW_SERIALIZE_OK == stat) {
225  // if there was still room, do nothing move on to the next channel in the packet
226  } else // any other status is an assert, since it shouldn't happen
227  {
228  FW_ASSERT(0, static_cast<FwAssertArgType>(stat));
229  }
230  // flag as updated
231  p_entry->updated = false;
232  } // end if entry was updated
233  } // end for each entry
234 
235  // send remnant entries
236  if (pkt.getNumEntries() > 0) {
237  this->PktSend_out(0, pkt.getBuffer(), 0);
238  }
239 } // end run handler
240 
241 } // namespace Svc
Serialization/Deserialization operation was successful.
void resetSer()
reset to beginning of buffer to reuse for serialization
TlmChan(const char *compName)
Definition: TlmChan.cpp:26
No room left in the buffer to serialize data.
void PktSend_out(FwIndexType portNum, Fw::ComBuffer &data, U32 context)
Invoke output port PktSend.
Fw::ComBuffer & getBuffer()
get buffer to send to the ground
Definition: TlmPacket.cpp:55
Auto-generated base for TlmChan component.
Component that stores telemetry channel values.
FwSizeType getNumEntries()
get the number of packets added via addValue()
Definition: TlmPacket.cpp:51
Comparison
The type of a comparison result.
Definition: Time.hpp:51
virtual ~TlmChan()
Definition: TlmChan.cpp:50
SerializeStatus
forward declaration for string
SerializeStatus addValue(FwChanIdType id, Time &timeTag, TlmBuffer &buffer)
Add telemetry value to buffer.
Definition: TlmPacket.cpp:63
virtual void lock()
Lock the guarded mutex.
bool isConnected_PktSend_OutputPort(FwIndexType portNum)
void pingOut_out(FwIndexType portNum, U32 key)
Invoke output port pingOut.
FwIdType FwChanIdType
The type of a telemetry channel identifier.
SerializeStatus resetPktSer()
Reset serialization of values. This should be done when starting to accumulate a new set of values...
Definition: TlmPacket.cpp:20
virtual FwChanIdType doHash(FwChanIdType id)
Definition: TlmChan.cpp:52
PlatformIndexType FwIndexType
virtual void unLock()
Unlock the guarded mutex.
static Comparison compare(const Time &time1, const Time &time2)
Definition: Time.cpp:107
RateGroupDivider component implementation.
#define FW_ASSERT(...)
Definition: Assert.hpp:14