GCC Code Coverage Report


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