F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
BufferAccumulator.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title BufferAccumulator.cpp
3 // \author bocchino
4 // \brief BufferAccumulator implementation
5 //
6 // \copyright
7 // Copyright (C) 2017 California Institute of Technology.
8 // ALL RIGHTS RESERVED. United States Government Sponsorship
9 // acknowledged.
10 //
11 // ======================================================================
12 
14 
15 #include <limits>
16 #include "Fw/Types/BasicTypes.hpp"
17 
18 namespace Svc {
19 
20 // ----------------------------------------------------------------------
21 // Construction, initialization, and destruction
22 // ----------------------------------------------------------------------
23 
24 BufferAccumulator ::BufferAccumulator(const char* const compName)
25  : BufferAccumulatorComponentBase(compName),
26  m_mode(BufferAccumulator_OpState::ACCUMULATE),
27  m_bufferMemory(nullptr),
28  m_bufferQueue(),
29  m_send(false),
30  m_waitForBuffer(false),
31  m_numWarnings(0u),
32  m_numDrained(0u),
33  m_numToDrain(0u),
34  m_opCode(),
35  m_cmdSeq(0u),
36  m_allocatorId(0) {}
37 
39 
40 // ----------------------------------------------------------------------
41 // Public methods
42 // ----------------------------------------------------------------------
43 
45  Fw::MemAllocator& allocator,
46  FwSizeType maxNumBuffers,
47  BufferAccumulator_OpState initialMode
49 ) {
50  this->m_allocatorId = identifier;
51  // Overflow protection
52  FW_ASSERT(maxNumBuffers > 0);
53  FW_ASSERT((std::numeric_limits<FwSizeType>::max() / maxNumBuffers) >= sizeof(Fw::Buffer));
54  FwSizeType memSize = static_cast<FwSizeType>(sizeof(Fw::Buffer) * maxNumBuffers);
55  bool recoverable = false;
56  // A null or short allocation would be placement-new'd through by the queue below
57  this->m_bufferMemory = static_cast<Fw::Buffer*>(allocator.checkedAllocate(identifier, memSize, recoverable));
58  m_bufferQueue.init(this->m_bufferMemory, maxNumBuffers);
59  this->m_mode = initialMode;
60  this->m_send = this->m_mode == BufferAccumulator_OpState::DRAIN;
61 }
62 
64  allocator.deallocate(static_cast<FwEnumStoreType>(this->m_allocatorId), this->m_bufferMemory);
65 }
66 
67 // ----------------------------------------------------------------------
68 // Handler implementations for user-defined typed input ports
69 // ----------------------------------------------------------------------
70 
71 void BufferAccumulator ::bufferSendInFill_handler(const FwIndexType portNum, Fw::Buffer& buffer) {
72  const bool status = this->m_bufferQueue.enqueue(buffer);
73  if (status) {
74  if (this->m_numWarnings > 0) {
76  }
77  this->m_numWarnings = 0;
78  } else {
79  if (this->m_numWarnings == 0) {
81  }
82  m_numWarnings++;
83  // The buffer is dropped; ownership must go back to its sender or the pool is depleted
84  this->bufferSendOutReturn_out(0, buffer);
85  }
86  if (this->m_send) {
87  this->sendStoredBuffer();
88  }
89 
90  this->tlmWrite_BA_NumQueuedBuffers(static_cast<U32>(this->m_bufferQueue.getSize()));
91 }
92 
93 void BufferAccumulator ::bufferSendInReturn_handler(const FwIndexType portNum, Fw::Buffer& buffer) {
94  this->bufferSendOutReturn_out(0, buffer);
95  this->m_waitForBuffer = false;
96  if ((this->m_mode == BufferAccumulator_OpState::DRAIN) || // we are draining ALL buffers
97  (this->m_numDrained < this->m_numToDrain)) { // OR we aren't done draining some buffers
98  // in a partial drain
99  this->m_send = true;
100  this->sendStoredBuffer();
101  }
102 }
103 
104 void BufferAccumulator ::pingIn_handler(const FwIndexType portNum, U32 key) {
105  this->pingOut_out(0, key);
106 }
107 
108 // ----------------------------------------------------------------------
109 // Command handler implementations
110 // ----------------------------------------------------------------------
111 
112 void BufferAccumulator ::BA_SetMode_cmdHandler(const FwOpcodeType opCode,
113  const U32 cmdSeq,
114  const BufferAccumulator_OpState& mode) {
115  // cancel an in-progress partial drain
116  if (this->m_numToDrain > 0) {
117  // reset counters for partial buffer drain
118  this->m_numToDrain = 0;
119  this->m_numDrained = 0;
120  // respond to the original command
121  this->cmdResponse_out(this->m_opCode, this->m_cmdSeq, Fw::CmdResponse::OK);
122  }
123 
124  this->m_mode = mode;
125  if (mode == BufferAccumulator_OpState::DRAIN) {
126  if (!this->m_waitForBuffer) {
127  this->m_send = true;
128  this->sendStoredBuffer();
129  }
130  } else {
131  this->m_send = false;
132  }
133  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
134 }
135 
136 void BufferAccumulator ::BA_DrainBuffers_cmdHandler(const FwOpcodeType opCode,
137  const U32 cmdSeq,
138  U32 numToDrain,
139  const BufferAccumulator_BlockMode& blockMode) {
140  if (this->m_numDrained < this->m_numToDrain) {
141  this->log_WARNING_HI_BA_StillDraining(static_cast<U32>(this->m_numDrained),
142  static_cast<U32>(this->m_numToDrain));
143  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::BUSY);
144  return;
145  }
146 
147  if (this->m_mode == BufferAccumulator_OpState::DRAIN) {
149  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
150  return;
151  }
152 
153  if (numToDrain == 0) {
155  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
156  return;
157  }
158 
159  this->m_opCode = opCode;
160  this->m_cmdSeq = cmdSeq;
161  this->m_numDrained = 0;
162  this->m_numToDrain = static_cast<FwSizeType>(numToDrain);
163 
164  if (blockMode == BufferAccumulator_BlockMode::NOBLOCK) {
165  FwSizeType numBuffers = this->m_bufferQueue.getSize();
166 
167  if (numBuffers < static_cast<FwSizeType>(numToDrain)) {
168  this->m_numToDrain = numBuffers;
169  this->log_WARNING_LO_BA_NonBlockDrain(static_cast<U32>(this->m_numToDrain), numToDrain);
170  }
171 
172  /* OK if there were 0 buffers queued, and we
173  * end up setting numToDrain to 0
174  */
175  if (0 == this->m_numToDrain) {
177  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
178  return;
179  }
180  }
181 
182  // We are still waiting for a buffer from last time
183  if (!this->m_waitForBuffer) {
184  this->m_send = true;
185  this->sendStoredBuffer(); // kick off the draining
186  }
187 }
188 
189 // ----------------------------------------------------------------------
190 // Private helper methods
191 // ----------------------------------------------------------------------
192 
193 void BufferAccumulator ::sendStoredBuffer() {
194  FW_ASSERT(this->m_send);
195  Fw::Buffer buffer;
196  if ((this->m_numToDrain == 0) || // we are draining ALL buffers
197  (this->m_numDrained < this->m_numToDrain)) { // OR we aren't done draining some buffers in a
198  // partial drain
199  const bool status = this->m_bufferQueue.dequeue(buffer);
200  if (status) { // a buffer was dequeued
201  this->m_numDrained++;
202  this->bufferSendOutDrain_out(0, buffer);
203  this->m_waitForBuffer = true;
204  this->m_send = false;
205  } else if (this->m_numToDrain > 0) {
206  this->log_WARNING_HI_BA_DrainStalled(static_cast<U32>(this->m_numDrained),
207  static_cast<U32>(this->m_numToDrain));
208  }
209  }
210 
211  /* This used to be "else if", but then you wait for all
212  * drained buffers in a partial drain to be RETURNED before returning OK.
213  * Correct thing is to return OK once they are SENT
214  */
215  if ((this->m_numToDrain > 0) && // we are doing a partial drain
216  (this->m_numDrained == this->m_numToDrain)) { // AND we just finished draining
217  //
218  this->log_ACTIVITY_HI_BA_PartialDrainDone(static_cast<U32>(this->m_numDrained));
219  // reset counters for partial buffer drain
220  this->m_numToDrain = 0;
221  this->m_numDrained = 0;
222  this->m_send = false;
223  this->cmdResponse_out(this->m_opCode, this->m_cmdSeq, Fw::CmdResponse::OK);
224  }
225 
226  this->tlmWrite_BA_NumQueuedBuffers(static_cast<U32>(this->m_bufferQueue.getSize()));
227 }
228 
229 } // namespace Svc
FwIdType FwOpcodeType
The type of a command opcode.
PlatformSizeType FwSizeType
I32 FwEnumStoreType
void bufferSendOutReturn_out(FwIndexType portNum, Fw::Buffer &fwBuffer) const
Invoke output port bufferSendOutReturn.
void log_ACTIVITY_HI_BA_PartialDrainDone(U32 numDrained) const
void log_WARNING_LO_BA_NonBlockDrain(U32 numWillDrain, U32 numReqDrain) const
BufferAccumulator(const char *const compName)
void log_WARNING_HI_BA_StillDraining(U32 numDrained, U32 numToDrain) const
void tlmWrite_BA_NumQueuedBuffers(U32 arg, Fw::Time _tlmTime=Fw::Time()) const
void pingOut_out(FwIndexType portNum, U32 key) const
Invoke output port pingOut.
void * checkedAllocate(const FwEnumStoreType identifier, FwSizeType &size, bool &recoverable, FwSizeType alignment=alignof(std::max_align_t))
Command successfully executed.
Memory Allocation base class.
PlatformIndexType FwIndexType
void bufferSendOutDrain_out(FwIndexType portNum, Fw::Buffer &fwBuffer) const
Invoke output port bufferSendOutDrain.
void deallocateQueue(Fw::MemAllocator &allocator)
Return allocated queue. Should be done during shutdown.
C++ header for working with basic fprime types.
void log_WARNING_HI_BA_DrainStalled(U32 numDrained, U32 numToDrain) const
Command failed validation.
RateGroupDivider component implementation.
virtual void deallocate(const FwEnumStoreType identifier, void *ptr)=0
Auto-generated base for BufferAccumulator component.
void allocateQueue(FwEnumStoreType identifier, Fw::MemAllocator &allocator, FwSizeType maxNumBuffers, BufferAccumulator_OpState initialMode=BufferAccumulator_OpState::ACCUMULATE)
#define FW_ASSERT(...)
Definition: Assert.hpp:14
void cmdResponse_out(FwOpcodeType opCode, U32 cmdSeq, Fw::CmdResponse response)
Emit command response.