GCC Code Coverage Report


Directory: /home/runner/work/fprime/fprime/Svc/DpWriter/
File: DpWriter.cpp
Date: 2026-09-03 22:13:38
Exec Total Coverage
Lines: 89 125 71.2%
Functions: 9 10 90.0%
Branches: 67 110 60.9%

Line Branch Exec Source
1 // ======================================================================
2 // \title DpWriter.cpp
3 // \author bocchino
4 // \brief cpp file for DpWriter component implementation class
5 // ======================================================================
6
7 #include "Svc/DpWriter/DpWriter.hpp"
8 #include "Fw/Com/ComPacket.hpp"
9 #include "Fw/FPrimeBasicTypes.hpp"
10 #include "Fw/Types/FileNameString.hpp"
11 #include "Fw/Types/Serializable.hpp"
12 #include "Os/File.hpp"
13 #include "Utils/Hash/Hash.hpp"
14 #include "config/DpCfg.hpp"
15
16 namespace Svc {
17
18 // ----------------------------------------------------------------------
19 // Construction, initialization, and destruction
20 // ----------------------------------------------------------------------
21
22
1/1
✓ Branch 2 taken 1 times.
1 DpWriter::DpWriter(const char* const compName) : DpWriterComponentBase(compName), m_dpFileNamePrefix() {}
23
24 2 DpWriter::~DpWriter() {}
25
26 1 void DpWriter::configure(const Fw::ConstStringBase& dpFileNamePrefix) {
27 1 this->m_dpFileNamePrefix = dpFileNamePrefix;
28 1 }
29
30 // ----------------------------------------------------------------------
31 // Handler implementations for user-defined typed input ports
32 // ----------------------------------------------------------------------
33
34 2 void DpWriter::bufferSendIn_handler(const FwIndexType portNum, Fw::Buffer& buffer) {
35 2 Fw::Success::T status = Fw::Success::SUCCESS;
36 // portNum is unused
37 (void)portNum;
38 // Update num buffers received
39 2 ++this->m_numBuffersReceived;
40 // Check that the buffer is valid
41
2/3
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if (!buffer.isValid()) {
42 this->log_WARNING_HI_InvalidBuffer();
43 status = Fw::Success::FAILURE;
44 }
45 // Check that the buffer is large enough to hold a data product packet
46
1/1
✓ Branch 1 taken 2 times.
2 const FwSizeType bufferSize = buffer.getSize();
47
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status == Fw::Success::SUCCESS) {
48
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (bufferSize < Fw::DpContainer::MIN_PACKET_SIZE) {
49 this->log_WARNING_HI_BufferTooSmallForPacket(static_cast<U32>(bufferSize),
50 Fw::DpContainer::MIN_PACKET_SIZE);
51
52 status = Fw::Success::FAILURE;
53 }
54 }
55 // Set up the container and check that the header hash is valid
56
1/1
✓ Branch 1 taken 2 times.
2 Fw::DpContainer container;
57
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status == Fw::Success::SUCCESS) {
58
1/1
✓ Branch 1 taken 2 times.
2 container.setBuffer(buffer);
59
1/1
✓ Branch 1 taken 2 times.
2 Utils::HashBuffer storedHash;
60
1/1
✓ Branch 1 taken 2 times.
2 Utils::HashBuffer computedHash;
61
1/1
✓ Branch 1 taken 2 times.
2 status = container.checkHeaderHash(storedHash, computedHash);
62
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (status != Fw::Success::SUCCESS) {
63 this->log_WARNING_HI_InvalidHeaderHash(static_cast<U32>(bufferSize), storedHash.asBigEndianU32(),
64 computedHash.asBigEndianU32());
65 }
66 2 }
67 // Deserialize the packet header
68
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status == Fw::Success::SUCCESS) {
69
1/1
✓ Branch 1 taken 2 times.
2 status = this->deserializePacketHeader(buffer, container);
70 }
71 // Check that the packet size fits in the buffer
72
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status == Fw::Success::SUCCESS) {
73
1/1
✓ Branch 1 taken 2 times.
2 const FwSizeType packetSize = container.getPacketSize();
74
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (bufferSize < packetSize) {
75 this->log_WARNING_HI_BufferTooSmallForData(static_cast<U32>(bufferSize), static_cast<U32>(packetSize));
76 status = Fw::Success::FAILURE;
77 }
78 }
79 // Perform the requested processing
80
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status == Fw::Success::SUCCESS) {
81
1/1
✓ Branch 1 taken 2 times.
2 this->performProcessing(container);
82 }
83 // Construct the file name
84
1/1
✓ Branch 1 taken 2 times.
2 Fw::FileNameString fileName;
85
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status == Fw::Success::SUCCESS) {
86 2 const FwDpIdType containerId = container.getId();
87
1/1
✓ Branch 1 taken 2 times.
2 const Fw::Time timeTag = container.getTimeTag();
88
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 8 taken 2 times.
2 const Fw::FormatStatus formatStatus = fileName.format(DP_FILENAME_FORMAT, this->m_dpFileNamePrefix.toChar(),
89 containerId, timeTag.getSeconds(), timeTag.getUSeconds());
90
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (formatStatus != Fw::FormatStatus::SUCCESS) {
91 this->log_WARNING_HI_FileNameFormatError(static_cast<Fw::StringFormatStatus::T>(formatStatus));
92 status = Fw::Success::FAILURE;
93 }
94 2 }
95 // Calculate and populate the file data checksum
96
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status == Fw::Success::SUCCESS) {
97
1/1
✓ Branch 1 taken 2 times.
2 container.updateDataHash();
98 }
99 // Write the file
100 2 FwSizeType fileSize = 0;
101
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status == Fw::Success::SUCCESS) {
102
1/1
✓ Branch 1 taken 2 times.
2 status = this->writeFile(container, fileName, fileSize);
103 }
104 // Send the DpWritten notification
105
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status == Fw::Success::SUCCESS) {
106
1/1
✓ Branch 1 taken 2 times.
2 this->sendNotification(container, fileName, fileSize);
107 }
108 // Deallocate the buffer
109
2/3
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 if (buffer.isValid()) {
110
1/1
✓ Branch 1 taken 2 times.
2 this->deallocBufferSendOut_out(0, buffer);
111 }
112 // Update the error count
113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (status != Fw::Success::SUCCESS) {
114 this->m_numErrors++;
115 }
116 2 }
117
118 59 void DpWriter::schedIn_handler(const FwIndexType portNum, U32 context) {
119 // portNum and context are not used
120 (void)portNum;
121 (void)context;
122 // Write telemetry
123
2/2
✓ Branch 1 taken 59 times.
✓ Branch 4 taken 59 times.
59 this->tlmWrite_NumBuffersReceived(this->m_numBuffersReceived);
124
2/2
✓ Branch 1 taken 59 times.
✓ Branch 4 taken 59 times.
59 this->tlmWrite_NumBytesWritten(this->m_numBytesWritten);
125
2/2
✓ Branch 1 taken 59 times.
✓ Branch 4 taken 59 times.
59 this->tlmWrite_NumSuccessfulWrites(this->m_numSuccessfulWrites);
126
2/2
✓ Branch 1 taken 59 times.
✓ Branch 4 taken 59 times.
59 this->tlmWrite_NumFailedWrites(this->m_numFailedWrites);
127
2/2
✓ Branch 1 taken 59 times.
✓ Branch 4 taken 59 times.
59 this->tlmWrite_NumErrors(this->m_numErrors);
128 59 }
129
130 // ----------------------------------------------------------------------
131 // Handler implementations for commands
132 // ----------------------------------------------------------------------
133
134 void DpWriter::CLEAR_EVENT_THROTTLE_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) {
135 // opCode and cmdSeq are not used
136 (void)opCode;
137 (void)cmdSeq;
138 // Clear throttling
139 this->log_WARNING_HI_BufferTooSmallForData_ThrottleClear();
140 this->log_WARNING_HI_BufferTooSmallForPacket_ThrottleClear();
141 this->log_WARNING_HI_FileOpenError_ThrottleClear();
142 this->log_WARNING_HI_FileWriteError_ThrottleClear();
143 this->log_WARNING_HI_InvalidBuffer_ThrottleClear();
144 this->log_WARNING_HI_InvalidHeaderHash_ThrottleClear();
145 this->log_WARNING_HI_InvalidHeader_ThrottleClear();
146 // Return command response
147 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
148 }
149
150 // ----------------------------------------------------------------------
151 // Private helper functions
152 // ----------------------------------------------------------------------
153
154 2 Fw::Success::T DpWriter::deserializePacketHeader(Fw::Buffer& buffer, Fw::DpContainer& container) {
155 2 Fw::Success::T status = Fw::Success::SUCCESS;
156 2 container.setBuffer(buffer);
157 2 const Fw::SerializeStatus serialStatus = container.deserializeHeader();
158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (serialStatus != Fw::FW_SERIALIZE_OK) {
159 this->log_WARNING_HI_InvalidHeader(static_cast<U32>(buffer.getSize()), static_cast<U32>(serialStatus));
160 status = Fw::Success::FAILURE;
161 }
162 2 return status;
163 }
164
165 2 void DpWriter::performProcessing(Fw::DpContainer& container) {
166 // Get the buffer
167
1/1
✓ Branch 1 taken 2 times.
2 Fw::Buffer buffer = container.getBuffer();
168 // Get the bit mask for the processing types
169 2 const Fw::DpCfg::ProcType::SerialType procTypes = container.getProcTypes();
170 // Do the processing
171 2 bool did_process = false;
172
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 for (FwIndexType portNum = 0; portNum < NUM_PROCBUFFERSENDOUT_OUTPUT_PORTS; ++portNum) {
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if ((procTypes & (1 << portNum)) != 0) {
174 this->procBufferSendOut_out(portNum, buffer);
175 did_process = true;
176 }
177 }
178
179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (did_process) {
180 // Updated DpContainer object state with the returned value in the
181 // container buffer
182 Fw::SerializeStatus stat = container.deserializeHeader();
183 FW_ASSERT(stat == Fw::FW_SERIALIZE_OK, stat);
184
185 // Check that the buffer size is compatible with the packet size in
186 // the container header
187 FW_ASSERT(container.getPacketSize() <= buffer.getSize(),
188 static_cast<FwAssertArgType>(container.getPacketSize()),
189 static_cast<FwAssertArgType>(buffer.getSize()));
190
191 // Re-compute and serialize the container header into the buffer
192 container.updateHeaderHash();
193 container.serializeHeader();
194
195 // Shrink internal Fw::Buffer
196 container.shrinkBufferSize();
197 }
198 2 }
199
200 2 Fw::Success::T DpWriter::writeFile(const Fw::DpContainer& container,
201 const Fw::FileNameString& fileName,
202 FwSizeType& fileSize) {
203 2 Fw::Success::T status = Fw::Success::SUCCESS;
204 // Get the buffer
205
1/1
✓ Branch 1 taken 2 times.
2 Fw::Buffer buffer = container.getBuffer();
206 // Get the file size
207
1/1
✓ Branch 1 taken 2 times.
2 fileSize = container.getPacketSize();
208 // Open the file
209
1/1
✓ Branch 1 taken 2 times.
2 Os::File file;
210
1/1
✓ Branch 2 taken 2 times.
2 Os::File::Status fileStatus = file.open(fileName.toChar(), Os::File::OPEN_CREATE);
211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (fileStatus != Os::File::OP_OK) {
212 this->log_WARNING_HI_FileOpenError(static_cast<U32>(fileStatus), fileName);
213 status = Fw::Success::FAILURE;
214 }
215 // Write the file
216
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status == Fw::Success::SUCCESS) {
217 // Set write size to file size
218 // On entry to the write call, this is the number of bytes to write
219 // On return from the write call, this is the number of bytes written
220 2 FwSizeType writeSize = static_cast<FwSizeType>(fileSize);
221
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
2 fileStatus = file.write(buffer.getData(), writeSize);
222 // If a successful write occurred, then update the number of bytes written
223
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (fileStatus == Os::File::OP_OK) {
224 2 this->m_numBytesWritten += static_cast<U64>(writeSize);
225 }
226
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if ((fileStatus == Os::File::OP_OK) and (writeSize == static_cast<FwSizeType>(fileSize))) {
227 // If the write status is success, and the number of bytes written
228 // is the expected number, then record the success
229
1/1
✓ Branch 1 taken 2 times.
2 this->log_ACTIVITY_LO_FileWritten(static_cast<U32>(writeSize), fileName);
230 } else {
231 // Otherwise record the failure
232 this->log_WARNING_HI_FileWriteError(static_cast<U32>(fileStatus), static_cast<U32>(writeSize),
233 static_cast<U32>(fileSize), fileName);
234 status = Fw::Success::FAILURE;
235 }
236 }
237 // Update the count of successful or failed writes
238
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (status == Fw::Success::SUCCESS) {
239 2 this->m_numSuccessfulWrites++;
240 } else {
241 this->m_numFailedWrites++;
242 }
243 // Return the status
244 2 return status;
245 2 }
246
247 2 void DpWriter::sendNotification(const Fw::DpContainer& container,
248 const Fw::FileNameString& fileName,
249 FwSizeType fileSize) {
250
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (isConnected_dpWrittenOut_OutputPort(0)) {
251 // Get the priority
252 2 const FwDpPriorityType priority = container.getPriority();
253 2 this->dpWrittenOut_out(0, fileName, priority, fileSize);
254 }
255 2 }
256
257 } // end namespace Svc
258