GCC Code Coverage Report


Directory: ./
File: Svc/CmdSequencer/formats/AMPCSSequence.cpp
Date: 2026-09-03 21:13:48
Exec Total Coverage
Lines: 200 213 93.9%
Functions: 21 21 100.0%
Branches: 107 126 84.9%

Line Branch Exec Source
1 // \title AMPCSSequence.cpp
2 // \author Rob Bocchino
3 // \brief AMPCSSequence implementation
4 //
5 // \copyright
6 // Copyright (C) 2009-2018 California Institute of Technology.
7 // ALL RIGHTS RESERVED. United States Government Sponsorship
8 // acknowledged.
9 //
10 // ======================================================================
11
12 #include "Svc/CmdSequencer/formats/AMPCSSequence.hpp"
13 #include <Utils/Hash/Hash.hpp>
14 #include "Fw/Com/ComPacket.hpp"
15 #include "Fw/Types/Assert.hpp"
16 #include "Os/FileSystem.hpp"
17
18 namespace Svc {
19
20
3/3
✓ Branch 10 taken 74 times.
✓ Branch 16 taken 74 times.
✓ Branch 22 taken 74 times.
74 AMPCSSequence ::AMPCSSequence(CmdSequencerComponentImpl& component) : CmdSequencerComponentImpl::Sequence(component) {}
21
22 38 bool AMPCSSequence ::loadFile(const Fw::ConstStringBase& fileName) {
23 // Make sure there is a buffer allocated
24 38 FW_ASSERT(this->m_buffer.getBuffAddr() != nullptr);
25
26
1/1
✓ Branch 2 taken 38 times.
38 Fw::CmdStringArg crcFileName(fileName);
27
1/1
✓ Branch 2 taken 38 times.
38 crcFileName += ".CRC32";
28
29
1/1
✓ Branch 6 taken 38 times.
38 this->m_header.m_timeBase = TimeBase::TB_DONT_CARE;
30 38 this->m_header.m_timeContext = FW_CONTEXT_DONT_CARE;
31
32
4/4
✓ Branch 4 taken 38 times.
✓ Branch 10 taken 35 times.
✓ Branch 12 taken 33 times.
✓ Branch 13 taken 2 times.
73 const bool status = this->readCRCFile(crcFileName) and this->getFileSize(fileName) and
33
6/6
✓ Branch 4 taken 33 times.
✓ Branch 6 taken 29 times.
✓ Branch 7 taken 4 times.
✓ Branch 12 taken 29 times.
✓ Branch 14 taken 28 times.
✓ Branch 15 taken 1 times.
33 this->readSequenceFile(fileName) and this->validateCRC() and
34
7/8
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 3 times.
✓ Branch 11 taken 28 times.
✓ Branch 13 taken 28 times.
✗ Branch 14 not taken.
✓ Branch 19 taken 28 times.
✓ Branch 21 taken 26 times.
✓ Branch 22 taken 2 times.
73 this->m_header.validateTime(this->m_component) and this->validateRecords();
35
36 38 return status;
37 38 }
38
39 38 bool AMPCSSequence ::readCRCFile(Fw::CmdStringArg& crcFileName) {
40 bool result;
41
42 38 this->setFileName(crcFileName);
43
44 38 Os::File::Status status = this->m_crcFile.open(crcFileName.toChar(), Os::File::OPEN_READ);
45
46
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 3 times.
38 if (status == Os::File::OP_OK) {
47
2/4
✓ Branch 4 taken 35 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 35 times.
✗ Branch 11 not taken.
35 result = this->readCRC() and this->deserializeCRC();
48
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 } else if (status == Os::File::DOESNT_EXIST) {
49 2 this->m_events.fileNotFound();
50 2 result = false;
51 } else {
52 1 this->m_events.fileReadError();
53 1 result = false;
54 }
55
56 38 this->m_crcFile.close();
57 38 return result;
58 }
59
60 35 bool AMPCSSequence ::getFileSize(const Fw::ConstStringBase& seqFileName) {
61 35 bool status = true;
62 35 FwSizeType fileSize;
63
1/1
✓ Branch 5 taken 35 times.
35 this->setFileName(seqFileName);
64
1/1
✓ Branch 7 taken 35 times.
35 const Os::FileSystem::Status fileStatus = Os::FileSystem::getFileSize(this->m_fileName.toChar(), fileSize);
65
4/4
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 1 times.
35 if (fileStatus == Os::FileSystem::OP_OK and fileSize >= static_cast<FwSizeType>(sizeof(this->m_sequenceHeader))) {
66 66 this->m_header.m_fileSize =
67 66 static_cast<U32>(fileSize - static_cast<FwSizeType>(sizeof(this->m_sequenceHeader)));
68 } else {
69
1/1
✓ Branch 4 taken 2 times.
2 this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_HEADER_SIZE, fileStatus);
70 2 status = false;
71 }
72 35 return status;
73 }
74
75 33 bool AMPCSSequence ::readSequenceFile(const Fw::ConstStringBase& seqFileName) {
76 bool result;
77
78 33 this->setFileName(seqFileName);
79 33 Os::File::Status status = this->m_sequenceFile.open(this->m_fileName.toChar(), Os::File::OPEN_READ);
80
81
1/2
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
33 if (status == Os::File::OP_OK) {
82 33 result = this->readOpenSequenceFile();
83 } else if (status == Os::File::DOESNT_EXIST) {
84 this->m_events.fileNotFound();
85 result = false;
86 } else {
87 this->m_events.fileReadError();
88 result = false;
89 }
90
91 33 this->m_sequenceFile.close();
92 33 return result;
93 }
94
95 29 bool AMPCSSequence ::validateCRC() {
96 29 bool result = true;
97 29 U32 computed = this->m_crc.finalize();
98
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 28 times.
29 if (this->m_crc.m_stored != computed) {
99 1 this->m_events.fileCRCFailure(this->m_crc.m_stored, computed);
100 1 result = false;
101 }
102 29 return result;
103 }
104
105 28 bool AMPCSSequence ::validateRecords() {
106 28 Fw::LinearBufferBase& buffer = this->m_buffer;
107
1/1
✓ Branch 2 taken 28 times.
28 Sequence::Record record;
108
109 // Deserialize all records and count the records
110
1/1
✓ Branch 7 taken 28 times.
28 const U32 loopBound = static_cast<U32>(buffer.getDeserializeSizeLeft());
111 28 U32 numRecords = 0;
112
1/2
✓ Branch 0 taken 147 times.
✗ Branch 1 not taken.
147 for (; numRecords < loopBound; ++numRecords) {
113
3/3
✓ Branch 7 taken 147 times.
✓ Branch 9 taken 26 times.
✓ Branch 10 taken 121 times.
147 if (not this->hasMoreRecords()) {
114 26 break;
115 }
116
1/1
✓ Branch 4 taken 121 times.
121 Fw::SerializeStatus status = this->deserializeRecord(record);
117
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 119 times.
121 if (status != Fw::FW_SERIALIZE_OK) {
118
1/1
✓ Branch 4 taken 2 times.
2 this->m_events.recordInvalid(numRecords, status);
119 2 return false;
120 }
121 }
122 // Set the number of records
123 26 this->m_header.m_numRecords = numRecords;
124 // Reset deserialization
125
1/1
✓ Branch 7 taken 26 times.
26 this->reset();
126
127 26 return true;
128 28 }
129
130 197 bool AMPCSSequence ::hasMoreRecords() const {
131 197 return this->m_buffer.getDeserializeSizeLeft() > 0;
132 }
133
134 46 void AMPCSSequence ::nextRecord(Sequence::Record& record) {
135 46 Fw::SerializeStatus status = this->deserializeRecord(record);
136 46 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
137 46 }
138
139 29 void AMPCSSequence ::reset() {
140 29 this->m_buffer.resetDeser();
141 29 }
142
143 34 void AMPCSSequence ::clear() {
144 34 this->m_buffer.resetSer();
145 34 }
146
147 35 bool AMPCSSequence ::readCRC() {
148 35 Os::File& file = this->m_crcFile;
149 35 Fw::LinearBufferBase& buffer = this->m_buffer;
150 35 bool status = true;
151 Fw::SerializeStatus ser_status;
152
153 35 FwSizeType readLen = static_cast<FwSizeType>(sizeof(U32));
154
1/1
✓ Branch 7 taken 35 times.
35 ser_status = buffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(readLen));
155 35 FW_ASSERT(ser_status == Fw::FW_SERIALIZE_OK, ser_status);
156
157 35 U8* const addr = buffer.getBuffAddr();
158
1/1
✓ Branch 4 taken 35 times.
35 Os::File::Status fileStatus = file.read(addr, readLen);
159
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (fileStatus != Os::File::OP_OK) {
161 this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_CRC, fileStatus);
162 status = false;
163 }
164
165 35 return status;
166 }
167
168 35 bool AMPCSSequence ::deserializeCRC() {
169 35 bool status = true;
170 35 Fw::SerializeStatus serializeStatus = this->m_buffer.deserializeTo(this->m_crc.m_stored);
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if (serializeStatus != Fw::FW_SERIALIZE_OK) {
172 this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_CRC, serializeStatus);
173 status = false;
174 }
175 35 return status;
176 }
177
178 33 bool AMPCSSequence ::readOpenSequenceFile() {
179 33 this->m_buffer.resetSer();
180 33 this->m_crc.init();
181 33 bool status = this->readSequenceHeader();
182
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1 times.
33 if (status) {
183 32 this->m_crc.update(this->m_sequenceHeader, sizeof(this->m_sequenceHeader));
184 32 status = this->readRecords();
185 }
186
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 4 times.
33 if (status) {
187 29 U8* const buffAddr = this->m_buffer.getBuffAddr();
188 29 const FwSizeType buffLen = this->m_buffer.getSize();
189 29 FW_ASSERT(buffLen == this->m_header.m_fileSize, static_cast<FwAssertArgType>(buffLen),
190 static_cast<FwAssertArgType>(this->m_header.m_fileSize));
191 29 this->m_crc.update(buffAddr, buffLen);
192 }
193 33 return status;
194 }
195
196 33 bool AMPCSSequence ::readSequenceHeader() {
197 33 Os::File& file = this->m_sequenceFile;
198
199 33 bool status = true;
200
201 33 FwSizeType readLen = sizeof this->m_sequenceHeader;
202
1/1
✓ Branch 6 taken 33 times.
33 const Os::File::Status fileStatus = file.read(this->m_sequenceHeader, readLen);
203
204
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 32 times.
33 if (fileStatus != Os::File::OP_OK) {
205
1/1
✓ Branch 4 taken 1 times.
1 this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_HEADER, fileStatus);
206 1 status = false;
207 }
208
209
3/4
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 32 times.
33 if (status and readLen != sizeof this->m_sequenceHeader) {
210 this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_HEADER_SIZE, static_cast<I32>(readLen));
211 status = false;
212 }
213
214 33 return status;
215 }
216
217 32 bool AMPCSSequence ::readRecords() {
218 32 Os::File& file = this->m_sequenceFile;
219 32 const FwSizeType size = this->m_header.m_fileSize;
220 32 Fw::LinearBufferBase& buffer = this->m_buffer;
221 32 U8* const addr = buffer.getBuffAddr();
222
223 // Check file size
224
2/2
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 31 times.
32 if (size > this->m_buffer.getCapacity()) {
225
1/1
✓ Branch 4 taken 1 times.
1 this->m_events.fileSizeError(static_cast<U32>(size));
226 1 return false;
227 }
228
229 31 FwSizeType readLen = size;
230
1/1
✓ Branch 4 taken 31 times.
31 const Os::File::Status fileStatus = file.read(addr, readLen);
231 // Check read status
232
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 30 times.
31 if (fileStatus != Os::File::OP_OK) {
233
1/1
✓ Branch 4 taken 1 times.
1 this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_DATA, fileStatus);
234 1 return false;
235 }
236 // Check read size
237 30 const FwSizeType readLenUint = static_cast<FwSizeType>(readLen);
238
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 29 times.
30 if (readLenUint != size) {
239
1/1
✓ Branch 4 taken 1 times.
1 this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_DATA_SIZE, static_cast<I32>(readLen));
240 1 return false;
241 }
242 // set buffer size
243
1/1
✓ Branch 7 taken 29 times.
29 const Fw::SerializeStatus serializeStatus = buffer.setBuffLen(size);
244 29 FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, serializeStatus);
245 29 return true;
246 }
247
248 167 Fw::SerializeStatus AMPCSSequence ::deserializeRecord(Sequence::Record& record) {
249 167 Record::CmdLength::t cmdLength;
250
251
1/1
✓ Branch 6 taken 167 times.
167 Fw::SerializeStatus status = this->deserializeTimeFlag(record.m_descriptor);
252
253
2/2
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 2 times.
167 if (status == Fw::FW_SERIALIZE_OK) {
254
1/1
✓ Branch 7 taken 165 times.
165 status = this->deserializeTime(record.m_timeTag);
255 }
256
2/2
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 2 times.
167 if (status == Fw::FW_SERIALIZE_OK) {
257
1/1
✓ Branch 4 taken 165 times.
165 status = this->deserializeCmdLength(cmdLength);
258 }
259
2/2
✓ Branch 0 taken 165 times.
✓ Branch 1 taken 2 times.
167 if (status == Fw::FW_SERIALIZE_OK) {
260
1/1
✓ Branch 7 taken 165 times.
165 status = this->translateCommand(record.m_command, cmdLength);
261 }
262
263 167 return status;
264 }
265
266 167 Fw::SerializeStatus AMPCSSequence ::deserializeTimeFlag(Sequence::Record::Descriptor& descriptor) {
267 167 Fw::LinearBufferBase& buffer = this->m_buffer;
268 167 Record::TimeFlag::Serial::t timeFlagSerial;
269
1/1
✓ Branch 7 taken 167 times.
167 Fw::SerializeStatus status = buffer.deserializeTo(timeFlagSerial);
270
1/2
✓ Branch 0 taken 167 times.
✗ Branch 1 not taken.
167 if (status == Fw::FW_SERIALIZE_OK) {
271
3/3
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 161 times.
✓ Branch 2 taken 2 times.
167 switch (timeFlagSerial) {
272 4 case Record::TimeFlag::ABSOLUTE:
273 4 descriptor = Sequence::Record::ABSOLUTE;
274 4 break;
275 161 case Record::TimeFlag::RELATIVE:
276 161 descriptor = Sequence::Record::RELATIVE;
277 161 break;
278 2 default:
279 2 status = Fw::FW_DESERIALIZE_FORMAT_ERROR;
280 2 break;
281 }
282 }
283 167 return status;
284 }
285
286 165 Fw::SerializeStatus AMPCSSequence ::deserializeTime(Fw::Time& timeTag) {
287 165 Record::Time::t time;
288 165 Fw::LinearBufferBase& buffer = this->m_buffer;
289
1/1
✓ Branch 7 taken 165 times.
165 Fw::SerializeStatus status = buffer.deserializeTo(time);
290
1/2
✓ Branch 0 taken 165 times.
✗ Branch 1 not taken.
165 if (status == Fw::FW_SERIALIZE_OK) {
291
1/1
✓ Branch 4 taken 165 times.
165 timeTag.set(time, 0);
292 }
293 165 return status;
294 }
295
296 165 Fw::SerializeStatus AMPCSSequence ::deserializeCmdLength(Record::CmdLength::t& cmdLength) {
297 165 Fw::LinearBufferBase& buffer = this->m_buffer;
298 165 Fw::SerializeStatus status = buffer.deserializeTo(cmdLength);
299
3/6
✓ Branch 0 taken 165 times.
✗ Branch 1 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 165 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 165 times.
165 if (status == Fw::FW_SERIALIZE_OK and cmdLength > buffer.getDeserializeSizeLeft()) {
300 // Not enough data left
301 status = Fw::FW_DESERIALIZE_SIZE_MISMATCH;
302 }
303 // The translated record occupies the packet descriptor, the zero-extension bytes prepended to
304 // the two-byte AMPCS opcode, and the record body. It must fit the ComBuffer capacity:
305 // SERIALIZED_SIZE is the capacity plus the stored length, and comparing against it admits
306 // records that setBuffLen then rejects, asserting in translateCommand
307 165 const FwSizeType translatedSize =
308 165 sizeof(FwPacketDescriptorType) + (sizeof(FwOpcodeType) - 2) + static_cast<FwSizeType>(cmdLength);
309
2/4
✓ Branch 0 taken 165 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 165 times.
165 if (status == Fw::FW_SERIALIZE_OK and translatedSize > FW_COM_BUFFER_MAX_SIZE) {
310 // Record size is too big for com buffer
311 status = Fw::FW_DESERIALIZE_SIZE_MISMATCH;
312 }
313 165 return status;
314 }
315
316 165 Fw::SerializeStatus AMPCSSequence ::translateCommand(Fw::ComBuffer& comBuffer, const Record::CmdLength::t cmdLength) {
317 165 Fw::LinearBufferBase& buffer = this->m_buffer;
318
1/1
✓ Branch 5 taken 165 times.
165 comBuffer.resetSer();
319 // Serialize the command packet descriptor
320 165 const FwPacketDescriptorType cmdDescriptor = Fw::ComPacketType::FW_PACKET_COMMAND;
321
1/1
✓ Branch 5 taken 165 times.
165 Fw::SerializeStatus status = comBuffer.serializeFrom(cmdDescriptor);
322 165 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
323 // Zero-extend the two-byte AMPCS opcode by (sizeof(FwOpcodeType) - 2) bytes
324 static_assert(sizeof(FwOpcodeType) >= 2, "FwOpcodeType must be at least two bytes wide");
325 165 U32 sizeOfZeros = 0;
326 165 const FwIndexType bytesToExtend = sizeof(FwOpcodeType) - 2;
327 165 const U8 zeros = 0;
328
2/2
✓ Branch 0 taken 330 times.
✓ Branch 1 taken 165 times.
495 for (FwIndexType i = 0; i < bytesToExtend; i++) {
329
1/1
✓ Branch 5 taken 330 times.
330 status = comBuffer.serializeFrom(zeros);
330 330 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
331 330 sizeOfZeros += static_cast<U32>(sizeof(zeros));
332 }
333 // Set the buffer length
334
1/1
✓ Branch 5 taken 165 times.
165 const U32 fixedBuffLen = static_cast<U32>(comBuffer.getSize());
335 165 FW_ASSERT(fixedBuffLen == sizeof(cmdDescriptor) + sizeOfZeros, static_cast<FwAssertArgType>(fixedBuffLen));
336 165 const U32 totalBuffLen = fixedBuffLen + cmdLength;
337
1/1
✓ Branch 5 taken 165 times.
165 status = comBuffer.setBuffLen(totalBuffLen);
338 165 FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
339 // Copy the opcode and argument bytes
340 165 FwSizeType size = cmdLength;
341 165 U8* const addr = comBuffer.getBuffAddr();
342 165 FW_ASSERT(addr != nullptr);
343 // true means "don't serialize the length"
344
1/1
✓ Branch 8 taken 165 times.
165 status = buffer.deserializeTo(&addr[fixedBuffLen], size, Fw::Serialization::OMIT_LENGTH);
345 165 return status;
346 }
347
348 } // namespace Svc
349