GCC Code Coverage Report


Directory: ./
File: Svc/FrameAccumulator/FrameAccumulator.cpp
Date: 2026-09-03 22:12:29
Exec Total Coverage
Lines: 78 89 87.6%
Functions: 8 8 100.0%
Branches: 37 53 69.8%

Line Branch Exec Source
1 // ======================================================================
2 // \title FrameAccumulator.cpp
3 // \author mstarch
4 // \brief cpp file for FrameAccumulator component implementation class
5 // ======================================================================
6
7 #include "Svc/FrameAccumulator/FrameAccumulator.hpp"
8 #include "Fw/FPrimeBasicTypes.hpp"
9 #include "Fw/Types/Assert.hpp"
10
11 namespace Svc {
12
13 // ----------------------------------------------------------------------
14 // Component construction and destruction
15 // ----------------------------------------------------------------------
16
17 1 FrameAccumulator ::FrameAccumulator(const char* const compName)
18 : FrameAccumulatorComponentBase(compName),
19 1 m_detector(nullptr),
20 1 m_memoryAllocator(nullptr),
21 1 m_memory(nullptr),
22
1/1
✓ Branch 2 taken 1 times.
1 m_allocatorId(0) {}
23
24 2 FrameAccumulator ::~FrameAccumulator() {}
25
26 1 void FrameAccumulator ::configure(const FrameDetector& detector,
27 FwEnumStoreType allocationId,
28 Fw::MemAllocator& allocator,
29 FwSizeType store_size) {
30 1 bool recoverable = false;
31
1/1
✓ Branch 1 taken 1 times.
1 U8* const data = static_cast<U8*>(allocator.allocate(allocationId, store_size, recoverable));
32 1 FW_ASSERT(data != nullptr);
33
1/1
✓ Branch 1 taken 1 times.
1 m_inRing.setup(data, store_size);
34
35 1 this->m_detector = &detector;
36 1 this->m_allocatorId = allocationId;
37 1 this->m_memoryAllocator = &allocator;
38 1 this->m_memory = data;
39 1 }
40
41 1 void FrameAccumulator ::cleanup() {
42 // If configuration happened, we must deallocate
43
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (this->m_memoryAllocator != nullptr) {
44 1 this->m_memoryAllocator->deallocate(this->m_allocatorId, this->m_memory);
45 1 this->m_memory = nullptr;
46 }
47 1 }
48
49 // ----------------------------------------------------------------------
50 // Handler implementations for user-defined typed input ports
51 // ----------------------------------------------------------------------
52
53 614 void FrameAccumulator ::dataIn_handler(FwIndexType portNum, Fw::Buffer& buffer, const ComCfg::FrameContext& context) {
54 // Check whether there is data to process
55
1/2
✓ Branch 1 taken 614 times.
✗ Branch 2 not taken.
614 if (buffer.isValid()) {
56 614 this->processBuffer(buffer, context);
57 }
58 // Return ownership of the incoming data
59 614 this->dataReturnOut_out(0, buffer, context);
60 614 }
61
62 614 void FrameAccumulator ::processBuffer(Fw::Buffer& buffer, const ComCfg::FrameContext& context) {
63
1/1
✓ Branch 1 taken 614 times.
614 const FwSizeType bufferSize = buffer.getSize();
64
1/1
✓ Branch 1 taken 614 times.
614 U8* const bufferData = buffer.getData();
65 // Current offset into buffer
66 614 FwSizeType offset = 0;
67 // Remaining data in buffer
68 614 FwSizeType remaining = bufferSize;
69
70
1/2
✓ Branch 0 taken 1228 times.
✗ Branch 1 not taken.
1228 for (FwSizeType i = 0; i < bufferSize; ++i) {
71 // If there is no data left or no space, exit the loop
72
6/7
✓ Branch 0 taken 614 times.
✓ Branch 1 taken 614 times.
✓ Branch 3 taken 614 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 614 times.
✓ Branch 7 taken 614 times.
✓ Branch 8 taken 614 times.
1228 if (remaining == 0 || this->m_inRing.get_free_size() == 0) {
73 614 break;
74 }
75 // Compute the size of data to serialize
76
1/1
✓ Branch 1 taken 614 times.
614 const FwSizeType ringFreeSize = this->m_inRing.get_free_size();
77
1/2
✓ Branch 0 taken 614 times.
✗ Branch 1 not taken.
614 const FwSizeType serSize = (ringFreeSize <= remaining) ? ringFreeSize : remaining;
78 // Serialize data into the ring buffer
79
1/1
✓ Branch 1 taken 614 times.
614 const Fw::SerializeStatus status = this->m_inRing.serialize(&bufferData[offset], serSize);
80 // If data does not fit, there is a coding error
81 614 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(status),
82 static_cast<FwAssertArgType>(offset), static_cast<FwAssertArgType>(serSize));
83 // Process the data
84
1/1
✓ Branch 1 taken 614 times.
614 this->processRing(context);
85 // Update buffer offset and remaining
86 614 offset += serSize;
87 614 remaining -= serSize;
88 }
89 // Either all the bytes from the data buffer must be processed, or the ring must be full
90 614 FW_ASSERT(remaining == 0 || this->m_inRing.get_free_size() == 0, static_cast<FwAssertArgType>(remaining));
91 614 }
92
93 614 void FrameAccumulator ::processRing(const ComCfg::FrameContext& context) {
94 614 FW_ASSERT(this->m_detector != nullptr);
95
96 // The number of remaining bytes in the ring buffer
97 614 FwSizeType remaining = 0;
98 // The protocol status
99 614 FrameDetector::Status status = FrameDetector::Status::FRAME_DETECTED;
100 // The ring buffer capacity
101 614 const FwSizeType ringCapacity = this->m_inRing.get_capacity();
102
103 // Process the ring buffer looking for at least the header
104
1/2
✓ Branch 0 taken 6394 times.
✗ Branch 1 not taken.
6394 for (FwSizeType i = 0; i < ringCapacity; i++) {
105 // Get the number of bytes remaining in the ring buffer
106
1/1
✓ Branch 1 taken 6394 times.
6394 remaining = this->m_inRing.get_allocated_size();
107 // If there are none, we are done
108
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 6250 times.
6394 if (remaining == 0) {
109 614 break;
110 }
111 // size_out is a return variable we initialize to zero, but it should be overwritten
112 6250 FwSizeType size_out = 0;
113 // Attempt to detect the frame without changing the circular buffer
114
1/1
✓ Branch 1 taken 6250 times.
6250 status = this->m_detector->detect(this->m_inRing, size_out);
115 // Detect must not consume data in the ring buffer
116 6250 FW_ASSERT(m_inRing.get_allocated_size() == remaining,
117 static_cast<FwAssertArgType>(m_inRing.get_allocated_size()), static_cast<FwAssertArgType>(remaining));
118
119 // Drop frames that are too large to handle (can't fit in the accumulation circular buffer)
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6250 times.
6250 if (size_out > ringCapacity) {
121 // Detector reports a size_out larger than the accumulation buffer capacity, we will never be able
122 // to process it. Log a warning and discard a byte, then keep iterating to look for a new frame
123 this->log_WARNING_HI_FrameDetectionSizeError(size_out);
124 // Discard a single byte of data and start again
125 (void)this->m_inRing.rotate(1);
126 FW_ASSERT(m_inRing.get_allocated_size() == remaining - 1,
127 static_cast<FwAssertArgType>(m_inRing.get_allocated_size()),
128 static_cast<FwAssertArgType>(remaining));
129 continue;
130 }
131
132 // On successful detection, consume data from the ring buffer and place it into an allocated frame
133
2/2
✓ Branch 0 taken 146 times.
✓ Branch 1 taken 6104 times.
6250 if (status == FrameDetector::FRAME_DETECTED) {
134 // size_out must be set (non-zero) and must fit within the remaining data
135 146 FW_ASSERT(size_out != 0);
136 146 FW_ASSERT(size_out <= remaining, static_cast<FwAssertArgType>(size_out),
137 static_cast<FwAssertArgType>(remaining));
138
1/1
✓ Branch 1 taken 146 times.
146 Fw::Buffer buffer = this->bufferAllocate_out(0, size_out);
139
2/3
✓ Branch 1 taken 146 times.
✓ Branch 3 taken 146 times.
✗ Branch 4 not taken.
146 if (buffer.isValid()) {
140 // Copy data out of ring buffer into the allocated buffer
141
2/2
✓ Branch 1 taken 146 times.
✓ Branch 4 taken 146 times.
146 Fw::SerializeStatus serialize_status = this->m_inRing.peek(buffer.getData(), size_out);
142
1/1
✓ Branch 1 taken 146 times.
146 buffer.setSize(size_out);
143 146 FW_ASSERT(serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK);
144 // Consume (rotate) the data from the ring buffer
145
1/1
✓ Branch 1 taken 146 times.
146 serialize_status = this->m_inRing.rotate(size_out);
146 146 FW_ASSERT(serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK);
147 146 FW_ASSERT(m_inRing.get_allocated_size() == remaining - size_out,
148 static_cast<FwAssertArgType>(m_inRing.get_allocated_size()),
149 static_cast<FwAssertArgType>(remaining), static_cast<FwAssertArgType>(size_out));
150
1/1
✓ Branch 1 taken 146 times.
146 this->dataOut_out(0, buffer, context);
151 } else {
152 // No buffer is available
153 this->log_WARNING_HI_NoBufferAvailable();
154 // In the case where no buffer is available and the circular buffer is full, we have to drop the buffer
155 // as there is no other way to retry. Without dropping it, the back pressure would assert the
156 // processing call, which is built on the assumption that at least one byte would process
157 if (this->m_inRing.get_free_size() == 0) {
158 // Discard the whole frame as a last attempt to keep the system afloat
159 Fw::SerializeStatus serialize_status = this->m_inRing.rotate(size_out);
160 FW_ASSERT(serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK);
161 FW_ASSERT(m_inRing.get_allocated_size() == remaining - size_out,
162 static_cast<FwAssertArgType>(m_inRing.get_allocated_size()),
163 static_cast<FwAssertArgType>(remaining), static_cast<FwAssertArgType>(size_out));
164 this->log_WARNING_HI_FrameDetectionValidFrameDropped();
165 }
166 break;
167 }
168 146 }
169 // More data needed
170
2/2
✓ Branch 0 taken 470 times.
✓ Branch 1 taken 5634 times.
6104 else if (status == FrameDetector::MORE_DATA_NEEDED) {
171 // Detection should report "more is needed" and set size_out to something larger than available data
172 470 FW_ASSERT(size_out > remaining, static_cast<FwAssertArgType>(size_out),
173 static_cast<FwAssertArgType>(remaining));
174 // Break out of loop: suspend detection until we receive another buffer
175 470 break;
176 }
177 // No frame was detected or an unknown status was received
178 else {
179 // Discard a single byte of data and start again
180
1/1
✓ Branch 1 taken 5634 times.
5634 (void)this->m_inRing.rotate(1);
181 5634 FW_ASSERT(m_inRing.get_allocated_size() == remaining - 1,
182 static_cast<FwAssertArgType>(m_inRing.get_allocated_size()),
183 static_cast<FwAssertArgType>(remaining));
184 }
185 }
186 614 }
187
188 146 void FrameAccumulator ::dataReturnIn_handler(FwIndexType portNum,
189 Fw::Buffer& fwBuffer,
190 const ComCfg::FrameContext& context) {
191 // Frame buffer ownership is returned to the component. Component had allocated with a buffer manager,
192 // so we return it to the buffer manager for deallocation
193 146 this->bufferDeallocate_out(0, fwBuffer);
194 146 }
195
196 } // namespace Svc
197