F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
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.
Definition: Time.hpp:9
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:56
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:52
Comparison
The type of a comparison result.
Definition: Time.hpp:48
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:64
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:21
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:159
RateGroupDivider component implementation.
#define FW_ASSERT(...)
Definition: Assert.hpp:14