GCC Code Coverage Report


Directory: Svc/CmdSequencer/
File: formats/AMPCSSequence.cpp
Date: 2026-10-14 21:18:12
Exec Total Coverage
Lines: 203 216 94.0%
Functions: 21 21 100.0%
Branches: 111 129 86.0%

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