GCC Code Coverage Report


Directory: /home/runner/work/fprime/fprime/Svc/ComLoggerDp/
File: ComLoggerDp.cpp
Date: 2026-10-14 22:12:54
Exec Total Coverage
Lines: 0 123 0.0%
Functions: 0 19 0.0%
Branches: 0 85 0.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title ComLoggerDp.cpp
3 // \author tcanham
4 // \brief cpp file for ComLoggerDp component implementation class
5 // ======================================================================
6
7 #include "Svc/ComLoggerDp/ComLoggerDp.hpp"
8 #include "Fw/FPrimeBasicTypes.hpp"
9
10 namespace Svc {
11
12 // ----------------------------------------------------------------------
13 // Component construction and destruction
14 // ----------------------------------------------------------------------
15
16 ✗ ComLoggerDp ::ComLoggerDp(const char* const compName) : ComLoggerDpComponentBase(compName) {}
17
18 ✗ ComLoggerDp ::~ComLoggerDp() {}
19
20 // ----------------------------------------------------------------------
21 // Public interface
22 // ----------------------------------------------------------------------
23
24 ✗ void ComLoggerDp ::configure(bool enabled,
25 FwSizeType packetsPerContainer,
26 FwDpPriorityType priority,
27 U32 flushTimeout) {
28 // Store flush timeout setting
29 ✗ this->m_flushTimeout = flushTimeout;
30
31 // If enabling, use the internal start function which validates parameters
32 ✗ if (enabled) {
33 // This will validate packetsPerContainer and set m_enabled
34 ✗ const Fw::Success started = this->startRecordingInternal(packetsPerContainer, priority);
35 ✗ FW_ASSERT(started == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(packetsPerContainer));
36 ✗ } else {
37 // Just disable if not enabling
38 ✗ this->m_enabled = false;
39 }
40 ✗ }
41
42 // ----------------------------------------------------------------------
43 // Handler implementations for typed input ports
44 // ----------------------------------------------------------------------
45
46 ✗ void ComLoggerDp ::comIn_handler(FwIndexType portNum, Fw::ComBuffer& data, U32 context) {
47 // portNum and context are unused
48 (void)portNum;
49 (void)context;
50
51 // If logging is not enabled, return immediately
52 ✗ if (!this->m_enabled) {
53 ✗ return;
54 }
55
56 // Reset inactivity counter - we received a packet
57 ✗ this->m_schedCallsSinceLastPacket = 0;
58
59 // Allocate container if needed
60 ✗ if ((this->m_currentPacketCount == 0) && (this->allocateAndSetupContainer() != Fw::Success::SUCCESS)) {
61 ✗ return;
62 }
63
64 // Get data from ComBuffer
65 ✗ const FwSizeType dataSize = data.getSize();
66 ✗ const U8* dataPtr = data.getBuffAddr();
67
68 // Serialize the packet
69 ✗ this->serializePacket(dataPtr, dataSize);
70
71 // Increment counters
72 ✗ ++this->m_currentPacketCount;
73 ✗ ++this->m_numBuffersLogged;
74
75 // Check if container has reached the limit
76 ✗ if (this->m_currentPacketCount >= this->m_packetsPerContainer) {
77 ✗ this->finalizeContainer();
78 }
79 }
80
81 ✗ void ComLoggerDp ::pingIn_handler(FwIndexType portNum, U32 key) {
82 // Return the key via pingOut port
83 ✗ this->pingOut_out(portNum, key);
84 ✗ }
85
86 ✗ void ComLoggerDp ::schedIn_handler(FwIndexType portNum, U32 context) {
87 // portNum and context are unused
88 (void)portNum;
89 (void)context;
90
91 // Check for auto-flush condition
92 // Note: m_flushTimeout > 0 check ensures auto-flush is disabled when timeout is 0
93 // Without this check, flushTimeout=0 would cause immediate flush (0 >= 0 is true)
94 ✗ if (this->m_enabled && this->m_currentPacketCount > 0 && this->m_flushTimeout > 0) {
95 ✗ ++this->m_schedCallsSinceLastPacket;
96
97 ✗ if (this->m_schedCallsSinceLastPacket >= this->m_flushTimeout) {
98 // Flush the partial container
99 ✗ this->finalizeContainer();
100 ✗ this->m_schedCallsSinceLastPacket = 0;
101 }
102 }
103
104 // Write telemetry
105 ✗ this->tlmWrite_LoggingEnabled(this->m_enabled);
106 ✗ this->tlmWrite_NumBuffersLogged(this->m_numBuffersLogged);
107 ✗ this->tlmWrite_NumBuffersDropped(this->m_numBuffersDropped);
108 ✗ this->tlmWrite_NumQueueDrops(static_cast<U32>(this->getNumMsgsDropped()));
109 ✗ }
110
111 ✗ void ComLoggerDp ::startRecordingIn_handler(FwIndexType portNum,
112 FwSizeType packetsPerContainer,
113 FwDpPriorityType priority) {
114 // portNum is unused
115 (void)portNum;
116
117 // Call internal helper function and check for validation failure
118 ✗ const Fw::Success success = this->startRecordingInternal(packetsPerContainer, priority);
119
120 // Log warning if validation failed
121 ✗ if (success != Fw::Success::SUCCESS) {
122 ✗ this->log_WARNING_LO_StartRecordingFailed(packetsPerContainer);
123 }
124 ✗ }
125
126 ✗ void ComLoggerDp ::stopRecordingIn_handler(FwIndexType portNum) {
127 // portNum is unused
128 (void)portNum;
129
130 // Call internal helper function
131 ✗ (void)this->stopRecordingInternal();
132 ✗ }
133
134 // ----------------------------------------------------------------------
135 // Private helper functions
136 // ----------------------------------------------------------------------
137
138 ✗ Fw::Success ComLoggerDp ::startRecordingInternal(FwSizeType packetsPerContainer, FwDpPriorityType priority) {
139 // The container (header + records) is requested through Fw.BufferGet, whose size argument is a U32,
140 // so reserve the header and bound the record count to what fits in the remaining U32 range
141 ✗ constexpr FwSizeType MAX_PACKETS_PER_CONTAINER =
142 (static_cast<FwSizeType>(std::numeric_limits<U32>::max()) - Fw::DpContainer::MIN_PACKET_SIZE) / RECORD_SIZE;
143 ✗ if ((packetsPerContainer == 0) || (packetsPerContainer > MAX_PACKETS_PER_CONTAINER)) {
144 // Disable logging on validation failure, flushing any partial container first
145 ✗ (void)this->sendContainerIfNonEmpty();
146 ✗ this->m_enabled = false;
147 ✗ return Fw::Success::FAILURE;
148 }
149
150 // If recording is already active and there's a partial container, send it before reconfiguring
151 ✗ (void)this->sendContainerIfNonEmpty();
152
153 // Store configuration
154 ✗ this->m_packetsPerContainer = packetsPerContainer;
155 ✗ this->m_priority = priority;
156
157 // Reset inactivity counter
158 ✗ this->m_schedCallsSinceLastPacket = 0;
159
160 // Enable logging
161 ✗ this->m_enabled = true;
162
163 // Log event
164 ✗ this->log_ACTIVITY_HI_ComDpStarted(packetsPerContainer);
165
166 ✗ return Fw::Success::SUCCESS;
167 }
168
169 ✗ ComLoggerDp_PartialContainerStatus ComLoggerDp ::stopRecordingInternal() {
170 // If there's a partial container, send it before stopping
171 ✗ const ComLoggerDp_PartialContainerStatus partialContainer = this->sendContainerIfNonEmpty();
172
173 // Disable logging
174 ✗ this->m_enabled = false;
175
176 // Reset inactivity counter
177 ✗ this->m_schedCallsSinceLastPacket = 0;
178
179 // Log event
180 ✗ this->log_ACTIVITY_HI_ComDpStopped(partialContainer);
181
182 ✗ return partialContainer;
183 ✗ }
184
185 ✗ void ComLoggerDp ::handleBufferDrop(U32 size) {
186 ✗ this->log_WARNING_HI_DpBufferError(size);
187 ✗ ++this->m_numBuffersDropped;
188 ✗ }
189
190 ✗ Fw::Success ComLoggerDp ::allocateAndSetupContainer() {
191 // Calculate data size needed for the requested number of packets
192 // Each record holds a sentry plus up to FW_COM_BUFFER_MAX_SIZE bytes
193 // Note: DpManager adds the container header overhead, so we only request the data size
194 ✗ const FwSizeType containerSize = this->m_packetsPerContainer * RECORD_SIZE;
195
196 // Get a container buffer
197 ✗ const Fw::Success status = this->dpGet_ComBuffContainer(containerSize, this->m_container);
198
199 // Check for allocation failure
200 ✗ if (status != Fw::Success::SUCCESS) {
201 ✗ this->handleBufferDrop(static_cast<U32>(containerSize));
202 ✗ return Fw::Success::FAILURE;
203 }
204
205 // Set the priority on the newly allocated container
206 ✗ this->m_container.setPriority(this->m_priority);
207 ✗ return Fw::Success::SUCCESS;
208 ✗ }
209
210 ✗ void ComLoggerDp ::serializePacket(const U8* dataPtr, FwSizeType dataSize) {
211 // Create buffer with sentry followed by ComBuffer data
212 // Use sizeof to get sentry size (supports user changing the constant type)
213 ✗ const FwSizeType sentrySize = sizeof(ComLoggerDpSentry);
214 ✗ const FwSizeType totalSize = sentrySize + dataSize;
215
216 // Use F Prime serialization to insert sentry value (handles endianness automatically)
217 ✗ Fw::ExternalSerializeBuffer serBuf(this->m_recordBuffer, sizeof(this->m_recordBuffer));
218 ✗ Fw::SerializeStatus serStatus = serBuf.serializeFrom(ComLoggerDpSentry);
219 ✗ FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
220
221 // Copy ComBuffer data after sentry
222 ✗ serStatus = serBuf.serializeFrom(dataPtr, dataSize, Fw::Serialization::OMIT_LENGTH);
223 ✗ FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
224
225 // Serialize the complete record into the container
226 ✗ serStatus = this->m_container.serializeRecord_ComBufferRecord(this->m_recordBuffer, totalSize);
227 ✗ FW_ASSERT(serStatus == Fw::FW_SERIALIZE_OK, serStatus);
228 ✗ }
229
230 ✗ ComLoggerDp_PartialContainerStatus ComLoggerDp ::sendContainerIfNonEmpty() {
231 // Send container if logging is enabled and it has any packets
232 // Handles both full and partial containers
233 ✗ if (this->m_enabled && this->m_currentPacketCount > 0) {
234 ✗ this->dpSend(this->m_container);
235 // Note: dpSend() invalidates the container; will allocate new one on next packet
236 ✗ this->m_currentPacketCount = 0;
237 ✗ return ComLoggerDp_PartialContainerStatus::SENT;
238 }
239 ✗ return ComLoggerDp_PartialContainerStatus::NOT_SENT;
240 }
241
242 ✗ void ComLoggerDp ::finalizeContainer() {
243 // Send the container (full or partial) and reset the packet count
244 ✗ (void)this->sendContainerIfNonEmpty();
245 ✗ }
246
247 // ----------------------------------------------------------------------
248 // Handler implementations for commands
249 // ----------------------------------------------------------------------
250
251 ✗ void ComLoggerDp ::StartComDp_cmdHandler(FwOpcodeType opCode,
252 U32 cmdSeq,
253 FwSizeType packetsPerContainer,
254 FwDpPriorityType priority) {
255 // Call internal helper function
256 ✗ const Fw::Success success = this->startRecordingInternal(packetsPerContainer, priority);
257
258 // Send command response
259 ✗ if (success == Fw::Success::SUCCESS) {
260 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
261 } else {
262 // Log warning event for validation failure (consistent with port path)
263 ✗ this->log_WARNING_LO_StartRecordingFailed(packetsPerContainer);
264 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR);
265 }
266 ✗ }
267
268 ✗ void ComLoggerDp ::UpdatePriority_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, FwDpPriorityType priority) {
269 // Store the new priority
270 ✗ this->m_priority = priority;
271
272 // Update priority if logging is enabled and there's an active container
273 ✗ if (this->m_enabled && (this->m_currentPacketCount > 0)) {
274 ✗ this->m_container.setPriority(this->m_priority);
275 }
276
277 // Log event
278 ✗ this->log_ACTIVITY_LO_PriorityUpdated(priority);
279
280 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
281 ✗ }
282
283 ✗ void ComLoggerDp ::StopComDp_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
284 // Call internal helper function
285 ✗ (void)this->stopRecordingInternal();
286
287 // Send command response
288 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
289 ✗ }
290
291 ✗ void ComLoggerDp ::CLEAR_COUNTERS_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
292 // Clear the NumBuffersLogged counter
293 ✗ this->m_numBuffersLogged = 0;
294
295 // Clear the NumBuffersDropped counter
296 ✗ this->m_numBuffersDropped = 0;
297
298 // Clear the DpBufferError event throttle
299 ✗ this->log_WARNING_HI_DpBufferError_ThrottleClear();
300
301 // Log event
302 ✗ this->log_ACTIVITY_LO_CountersCleared();
303
304 // Send command response
305 ✗ this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
306 ✗ }
307
308 } // namespace Svc
309