fprime/Svc/CmdSequencer/FPrimeSequence.cpp
Vince Woo 48e4720419
Created new SerialBufferBase as a parent of SerializeBufferBase (now renamed LinearBufferBase). (#4288)
* Created new SerialBufferBase as a parent of SerializeBufferBase. Renaming interface functions to be less confusing.

* Deprecating copyRawOffset. No direct use-cases in F' core.

* Make SerialBufferBase a true pure virtual interface.

* Changing Serializable to work with SerialBufferBase parent interface.

* Changing copyRaw and copyRawOffset to work with SerialBufferBase

* Updating documentation for SerialBufferBase usage

* Adding some documentation. Adding missing ASSERT in copyRaw. Fixing some bugs that new ASSERT uncovered.

* Renaming SerializeBufferBase to LinearBufferBase. Add a using declaration to maintain backwards compatability. Properly mark LinearBufferBase functions as override.

* Filling in the rest of the docstrings for the classes in Serializable

* Removing redundant virtual keyword on override function

* Applying clang formatting

* Incorporating PR comments

* Fix compile issues

* Bump version to alpha

* Format

* v

---------

Co-authored-by: M Starch <LeStarch@googlemail.com>
2025-11-06 16:23:20 -08:00

337 lines
12 KiB
C++

// ======================================================================
// \title FPrimeSequence.cpp
// \author Bocchino/Canham
// \brief CmdSequencerComponentImpl::FPrimeSequence implementation
//
// Copyright (C) 2009-2018 California Institute of Technology.
// ALL RIGHTS RESERVED. United States Government Sponsorship
// acknowledged.
// ======================================================================
#include "Fw/Types/Assert.hpp"
#include "Svc/CmdSequencer/CmdSequencerImpl.hpp"
extern "C" {
#include "Utils/Hash/libcrc/lib_crc.h"
}
namespace Svc {
CmdSequencerComponentImpl::FPrimeSequence::CRC ::CRC() : m_computed(INITIAL_COMPUTED_VALUE), m_stored(0) {}
void CmdSequencerComponentImpl::FPrimeSequence::CRC ::init() {
this->m_computed = INITIAL_COMPUTED_VALUE;
}
void CmdSequencerComponentImpl::FPrimeSequence::CRC ::update(const BYTE* buffer, FwSizeType bufferSize) {
FW_ASSERT(buffer);
for (FwSizeType index = 0; index < bufferSize; index++) {
this->m_computed = static_cast<U32>(update_crc_32(this->m_computed, static_cast<char>(buffer[index])));
}
}
void CmdSequencerComponentImpl::FPrimeSequence::CRC ::finalize() {
this->m_computed = ~this->m_computed;
}
CmdSequencerComponentImpl::FPrimeSequence ::FPrimeSequence(CmdSequencerComponentImpl& component)
: Sequence(component) {}
bool CmdSequencerComponentImpl::FPrimeSequence ::validateCRC() {
bool result = true;
if (this->m_crc.m_stored != this->m_crc.m_computed) {
this->m_events.fileCRCFailure(this->m_crc.m_stored, this->m_crc.m_computed);
result = false;
}
return result;
}
bool CmdSequencerComponentImpl::FPrimeSequence ::loadFile(const Fw::StringBase& fileName) {
// make sure there is a buffer allocated
FW_ASSERT(this->m_buffer.getBuffAddr());
this->setFileName(fileName);
const bool status = this->readFile() and this->validateCRC() and this->m_header.validateTime(this->m_component) and
this->validateRecords();
return status;
}
bool CmdSequencerComponentImpl::FPrimeSequence ::hasMoreRecords() const {
return this->m_buffer.getDeserializeSizeLeft() > 0;
}
void CmdSequencerComponentImpl::FPrimeSequence ::nextRecord(Record& record) {
Fw::SerializeStatus status = this->deserializeRecord(record);
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
}
void CmdSequencerComponentImpl::FPrimeSequence ::reset() {
this->m_buffer.resetDeser();
}
void CmdSequencerComponentImpl::FPrimeSequence ::clear() {
this->m_buffer.resetSer();
}
bool CmdSequencerComponentImpl::FPrimeSequence ::readFile() {
bool result;
Os::File::Status status = this->m_sequenceFile.open(this->m_fileName.toChar(), Os::File::OPEN_READ);
if (status == Os::File::OP_OK) {
result = this->readOpenFile();
} else if (status == Os::File::DOESNT_EXIST) {
this->m_events.fileNotFound();
result = false;
} else {
this->m_events.fileReadError();
result = false;
}
this->m_sequenceFile.close();
return result;
}
bool CmdSequencerComponentImpl::FPrimeSequence ::readOpenFile() {
U8* const buffAddr = this->m_buffer.getBuffAddr();
this->m_crc.init();
bool status = this->readHeader();
if (status) {
this->m_crc.update(buffAddr, Sequence::Header::SERIALIZED_SIZE);
status = this->deserializeHeader() and this->readRecordsAndCRC() and this->extractCRC();
}
if (status) {
const FwSizeType buffLen = this->m_buffer.getSize();
this->m_crc.update(buffAddr, buffLen);
this->m_crc.finalize();
}
return status;
}
bool CmdSequencerComponentImpl::FPrimeSequence ::readHeader() {
Os::File& file = this->m_sequenceFile;
Fw::SerializeBufferBase& buffer = this->m_buffer;
bool status = true;
FwSizeType readLen = Sequence::Header::SERIALIZED_SIZE;
const FwSizeType capacity = buffer.getCapacity();
FW_ASSERT(capacity >= readLen, static_cast<FwAssertArgType>(capacity), static_cast<FwAssertArgType>(readLen));
Os::File::Status fileStatus = file.read(buffer.getBuffAddr(), readLen);
if (fileStatus != Os::File::OP_OK) {
this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_HEADER, fileStatus);
status = false;
}
if (status and readLen != Sequence::Header::SERIALIZED_SIZE) {
this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_HEADER_SIZE, static_cast<I32>(readLen));
status = false;
}
if (status) {
const Fw::SerializeStatus serializeStatus = buffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(readLen));
FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, serializeStatus);
}
return status;
}
bool CmdSequencerComponentImpl::FPrimeSequence ::deserializeHeader() {
Fw::SerializeBufferBase& buffer = this->m_buffer;
Header& header = this->m_header;
// File size
Fw::SerializeStatus serializeStatus = buffer.deserializeTo(header.m_fileSize);
if (serializeStatus != Fw::FW_SERIALIZE_OK) {
this->m_events.fileInvalid(CmdSequencer_FileReadStage::DESER_SIZE, serializeStatus);
return false;
}
if (header.m_fileSize > buffer.getCapacity()) {
this->m_events.fileSizeError(header.m_fileSize);
return false;
}
// Number of records
serializeStatus = buffer.deserializeTo(header.m_numRecords);
if (serializeStatus != Fw::FW_SERIALIZE_OK) {
this->m_events.fileInvalid(CmdSequencer_FileReadStage::DESER_NUM_RECORDS, serializeStatus);
return false;
}
// Time base
TimeBase tbase;
serializeStatus = buffer.deserializeTo(tbase);
if (serializeStatus != Fw::FW_SERIALIZE_OK) {
this->m_events.fileInvalid(CmdSequencer_FileReadStage::DESER_TIME_BASE, serializeStatus);
return false;
}
header.m_timeBase = (tbase);
// Time context
serializeStatus = buffer.deserializeTo(header.m_timeContext);
if (serializeStatus != Fw::FW_SERIALIZE_OK) {
this->m_events.fileInvalid(CmdSequencer_FileReadStage::DESER_TIME_CONTEXT, serializeStatus);
return false;
}
return true;
}
bool CmdSequencerComponentImpl::FPrimeSequence ::readRecordsAndCRC() {
Os::File& file = this->m_sequenceFile;
const FwSizeType size = this->m_header.m_fileSize;
Fw::SerializeBufferBase& buffer = this->m_buffer;
FwSizeType readLen = size;
Os::File::Status fileStatus = file.read(buffer.getBuffAddr(), readLen);
// check read status
if (fileStatus != Os::File::OP_OK) {
this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_DATA, fileStatus);
return false;
}
// check read size
if (size != static_cast<FwSizeType>(readLen)) {
this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_DATA_SIZE, static_cast<I32>(readLen));
return false;
}
// set buffer size
Fw::SerializeStatus serializeStatus = buffer.setBuffLen(size);
FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, serializeStatus);
return true;
}
bool CmdSequencerComponentImpl::FPrimeSequence ::extractCRC() {
Fw::SerializeBufferBase& buffer = this->m_buffer;
U32& crc = this->m_crc.m_stored;
// Compute the data size
const FwSizeType buffSize = buffer.getSize();
const FwSizeType crcSize = sizeof(crc);
U8* const buffAddr = buffer.getBuffAddr();
if (buffSize < crcSize) {
this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_CRC, static_cast<I32>(buffSize));
return false;
}
FW_ASSERT(buffSize >= crcSize, static_cast<FwAssertArgType>(buffSize), crcSize);
const FwSizeType dataSize = buffSize - crcSize;
// Create a CRC buffer pointing at the CRC in the main buffer, after the data
Fw::ExternalSerializeBuffer crcBuff(&buffAddr[dataSize], crcSize);
Fw::SerializeStatus status = crcBuff.setBuffLen(crcSize);
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
// Deserialize the CRC from the CRC buffer
status = crcBuff.deserializeTo(crc);
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
// Set the main buffer size to the data size
status = buffer.setBuffLen(dataSize);
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
return true;
}
Fw::SerializeStatus CmdSequencerComponentImpl::FPrimeSequence ::deserializeRecord(Record& record) {
U32 recordSize;
Fw::SerializeStatus status = this->deserializeDescriptor(record.m_descriptor);
if (status == Fw::FW_SERIALIZE_OK and record.m_descriptor == Record::END_OF_SEQUENCE) {
return Fw::FW_SERIALIZE_OK;
}
if (status == Fw::FW_SERIALIZE_OK) {
status = this->deserializeTimeTag(record.m_timeTag);
}
if (status == Fw::FW_SERIALIZE_OK) {
status = this->deserializeRecordSize(recordSize);
}
if (status == Fw::FW_SERIALIZE_OK) {
status = this->copyCommand(record.m_command, recordSize);
}
return status;
}
Fw::SerializeStatus CmdSequencerComponentImpl::FPrimeSequence ::deserializeDescriptor(Record::Descriptor& descriptor) {
Fw::SerializeBufferBase& buffer = this->m_buffer;
U8 descEntry;
Fw::SerializeStatus status = buffer.deserializeTo(descEntry);
if (status != Fw::FW_SERIALIZE_OK) {
return status;
}
if (descEntry > Sequence::Record::END_OF_SEQUENCE) {
return Fw::FW_DESERIALIZE_FORMAT_ERROR;
}
descriptor = static_cast<Record::Descriptor>(descEntry);
return Fw::FW_SERIALIZE_OK;
}
Fw::SerializeStatus CmdSequencerComponentImpl::FPrimeSequence ::deserializeTimeTag(Fw::Time& timeTag) {
Fw::SerializeBufferBase& buffer = this->m_buffer;
U32 seconds, useconds;
Fw::SerializeStatus status = buffer.deserializeTo(seconds);
if (status == Fw::FW_SERIALIZE_OK) {
status = buffer.deserializeTo(useconds);
}
if (status == Fw::FW_SERIALIZE_OK) {
timeTag.set(seconds, useconds);
}
return status;
}
Fw::SerializeStatus CmdSequencerComponentImpl::FPrimeSequence ::deserializeRecordSize(U32& recordSize) {
Fw::SerializeBufferBase& buffer = this->m_buffer;
Fw::SerializeStatus status = buffer.deserializeTo(recordSize);
if (status == Fw::FW_SERIALIZE_OK and recordSize > buffer.getDeserializeSizeLeft()) {
// Not enough data left
status = Fw::FW_DESERIALIZE_SIZE_MISMATCH;
}
if (status == Fw::FW_SERIALIZE_OK and
recordSize + sizeof(FwPacketDescriptorType) > Fw::ComBuffer::SERIALIZED_SIZE) {
// Record size is too big for com buffer
status = Fw::FW_DESERIALIZE_SIZE_MISMATCH;
}
return status;
}
Fw::SerializeStatus CmdSequencerComponentImpl::FPrimeSequence ::copyCommand(Fw::ComBuffer& comBuffer,
const U32 recordSize) {
Fw::SerializeBufferBase& buffer = this->m_buffer;
comBuffer.resetSer();
FwSizeType size = recordSize;
Fw::SerializeStatus status = comBuffer.setBuffLen(recordSize);
FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status);
status = buffer.deserializeTo(comBuffer.getBuffAddr(), size, Fw::Serialization::OMIT_LENGTH);
return status;
}
bool CmdSequencerComponentImpl::FPrimeSequence ::validateRecords() {
Fw::SerializeBufferBase& buffer = this->m_buffer;
const U32 numRecords = this->m_header.m_numRecords;
Sequence::Record record;
if (numRecords == 0) {
this->m_events.noRecords();
return false;
}
// Deserialize all records
for (U32 recordNumber = 0; recordNumber < numRecords; recordNumber++) {
Fw::SerializeStatus status = this->deserializeRecord(record);
if (status != Fw::FW_SERIALIZE_OK) {
this->m_events.recordInvalid(recordNumber, status);
return false;
}
}
// Check there is no data left
const FwSizeType buffLeftSize = buffer.getDeserializeSizeLeft();
if (buffLeftSize > 0) {
this->m_events.recordMismatch(numRecords, static_cast<U32>(buffLeftSize));
return false;
}
// Rewind deserialization
buffer.resetDeser();
return true;
}
} // namespace Svc