GCC Code Coverage Report


Directory: Svc/FrameAccumulator/
File: FrameAccumulator.cpp
Date: 2026-09-03 21:17:59
Exec Total Coverage
Lines: 89 89 100.0%
Functions: 8 8 100.0%
Branches: 49 53 92.5%

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 11 FrameAccumulator ::FrameAccumulator(const char* const compName)
18 : FrameAccumulatorComponentBase(compName),
19 11 m_detector(nullptr),
20 11 m_memoryAllocator(nullptr),
21 11 m_memory(nullptr),
22
1/1
✓ Branch 9 taken 11 times.
22 m_allocatorId(0) {}
23
24 22 FrameAccumulator ::~FrameAccumulator() {}
25
26 11 void FrameAccumulator ::configure(const FrameDetector& detector,
27 FwEnumStoreType allocationId,
28 Fw::MemAllocator& allocator,
29 FwSizeType store_size) {
30 11 bool recoverable = false;
31
1/1
✓ Branch 6 taken 11 times.
11 U8* const data = static_cast<U8*>(allocator.allocate(allocationId, store_size, recoverable));
32 11 FW_ASSERT(data != nullptr);
33
1/1
✓ Branch 4 taken 11 times.
11 m_inRing.setup(data, store_size);
34
35 11 this->m_detector = &detector;
36 11 this->m_allocatorId = allocationId;
37 11 this->m_memoryAllocator = &allocator;
38 11 this->m_memory = data;
39 11 }
40
41 11 void FrameAccumulator ::cleanup() {
42 // If configuration happened, we must deallocate
43
1/2
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
11 if (this->m_memoryAllocator != nullptr) {
44 11 this->m_memoryAllocator->deallocate(this->m_allocatorId, this->m_memory);
45 11 this->m_memory = nullptr;
46 }
47 11 }
48
49 // ----------------------------------------------------------------------
50 // Handler implementations for user-defined typed input ports
51 // ----------------------------------------------------------------------
52
53 27 void FrameAccumulator ::dataIn_handler(FwIndexType portNum, Fw::Buffer& buffer, const ComCfg::FrameContext& context) {
54 // Check whether there is data to process
55
2/2
✓ Branch 4 taken 26 times.
✓ Branch 5 taken 1 times.
27 if (buffer.isValid()) {
56 26 this->processBuffer(buffer, context);
57 }
58 // Return ownership of the incoming data
59 27 this->dataReturnOut_out(0, buffer, context);
60 27 }
61
62 26 void FrameAccumulator ::processBuffer(Fw::Buffer& buffer, const ComCfg::FrameContext& context) {
63
1/1
✓ Branch 4 taken 26 times.
26 const FwSizeType bufferSize = buffer.getSize();
64
1/1
✓ Branch 4 taken 26 times.
26 U8* const bufferData = buffer.getData();
65 // Current offset into buffer
66 26 FwSizeType offset = 0;
67 // Remaining data in buffer
68 26 FwSizeType remaining = bufferSize;
69
70
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 3 times.
52 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 26 times.
✓ Branch 1 taken 23 times.
✓ Branch 6 taken 26 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 26 times.
✓ Branch 10 taken 23 times.
✓ Branch 11 taken 26 times.
49 if (remaining == 0 || this->m_inRing.get_free_size() == 0) {
73 23 break;
74 }
75 // Compute the size of data to serialize
76
1/1
✓ Branch 4 taken 26 times.
26 const FwSizeType ringFreeSize = this->m_inRing.get_free_size();
77
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 1 times.
26 const FwSizeType serSize = (ringFreeSize <= remaining) ? ringFreeSize : remaining;
78 // Serialize data into the ring buffer
79
1/1
✓ Branch 5 taken 26 times.
26 const Fw::SerializeStatus status = this->m_inRing.serialize(&bufferData[offset], serSize);
80 // If data does not fit, there is a coding error
81 26 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 4 taken 26 times.
26 this->processRing(context);
85 // Update buffer offset and remaining
86 26 offset += serSize;
87 26 remaining -= serSize;
88 }
89 // Either all the bytes from the data buffer must be processed, or the ring must be full
90 26 FW_ASSERT(remaining == 0 || this->m_inRing.get_free_size() == 0, static_cast<FwAssertArgType>(remaining));
91 26 }
92
93 26 void FrameAccumulator ::processRing(const ComCfg::FrameContext& context) {
94 26 FW_ASSERT(this->m_detector != nullptr);
95
96 // The number of remaining bytes in the ring buffer
97 26 FwSizeType remaining = 0;
98 // The protocol status
99 26 FrameDetector::Status status = FrameDetector::Status::FRAME_DETECTED;
100 // The ring buffer capacity
101 26 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 221 times.
✗ Branch 1 not taken.
221 for (FwSizeType i = 0; i < ringCapacity; i++) {
105 // Get the number of bytes remaining in the ring buffer
106
1/1
✓ Branch 4 taken 221 times.
221 remaining = this->m_inRing.get_allocated_size();
107 // If there are none, we are done
108
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 210 times.
221 if (remaining == 0) {
109 11 break;
110 }
111 // size_out is a return variable we initialize to zero, but it should be overwritten
112 210 FwSizeType size_out = 0;
113 // Attempt to detect the frame without changing the circular buffer
114
1/1
✓ Branch 26 taken 210 times.
210 status = this->m_detector->detect(this->m_inRing, size_out);
115 // Detect must not consume data in the ring buffer
116 210 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
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 207 times.
210 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
1/1
✓ Branch 5 taken 3 times.
3 this->log_WARNING_HI_FrameDetectionSizeError(size_out);
124 // Discard a single byte of data and start again
125
1/1
✓ Branch 4 taken 3 times.
3 (void)this->m_inRing.rotate(1);
126 3 FW_ASSERT(m_inRing.get_allocated_size() == remaining - 1,
127 static_cast<FwAssertArgType>(m_inRing.get_allocated_size()),
128 static_cast<FwAssertArgType>(remaining));
129 3 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 8 times.
✓ Branch 1 taken 199 times.
207 if (status == FrameDetector::FRAME_DETECTED) {
134 // size_out must be set (non-zero) and must fit within the remaining data
135 8 FW_ASSERT(size_out != 0);
136 8 FW_ASSERT(size_out <= remaining, static_cast<FwAssertArgType>(size_out),
137 static_cast<FwAssertArgType>(remaining));
138
1/1
✓ Branch 3 taken 8 times.
8 Fw::Buffer buffer = this->bufferAllocate_out(0, size_out);
139
3/3
✓ Branch 2 taken 8 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 1 times.
8 if (buffer.isValid()) {
140 // Copy data out of ring buffer into the allocated buffer
141
2/2
✓ Branch 5 taken 7 times.
✓ Branch 8 taken 7 times.
7 Fw::SerializeStatus serialize_status = this->m_inRing.peek(buffer.getData(), size_out);
142
1/1
✓ Branch 2 taken 7 times.
7 buffer.setSize(size_out);
143 7 FW_ASSERT(serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK);
144 // Consume (rotate) the data from the ring buffer
145
1/1
✓ Branch 4 taken 7 times.
7 serialize_status = this->m_inRing.rotate(size_out);
146 7 FW_ASSERT(serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK);
147 7 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 5 taken 7 times.
7 this->dataOut_out(0, buffer, context);
151 } else {
152 // No buffer is available
153
1/1
✓ Branch 5 taken 1 times.
1 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
2/3
✓ Branch 4 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1 if (this->m_inRing.get_free_size() == 0) {
158 // Discard the whole frame as a last attempt to keep the system afloat
159
1/1
✓ Branch 4 taken 1 times.
1 Fw::SerializeStatus serialize_status = this->m_inRing.rotate(size_out);
160 1 FW_ASSERT(serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK);
161 1 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
1/1
✓ Branch 5 taken 1 times.
1 this->log_WARNING_HI_FrameDetectionValidFrameDropped();
165 }
166 1 break;
167 }
168 8 }
169 // More data needed
170
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 185 times.
199 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 14 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 14 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 4 taken 185 times.
185 (void)this->m_inRing.rotate(1);
181 185 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 26 }
187
188 1 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 1 this->bufferDeallocate_out(0, fwBuffer);
194 1 }
195
196 } // namespace Svc
197