GCC Code Coverage Report


Directory: ./
File: Svc/CmdSequencer/formats/AMPCSSequence.cpp
Date: 2026-11-03 21:15:54
Exec Total Coverage
Lines: 207 220 94.1%
Functions: 21 21 100.0%
Branches: 116 134 86.6%

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