// ====================================================================== // \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(update_crc_32(this->m_computed, static_cast(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(capacity), static_cast(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(readLen)); status = false; } if (status) { const Fw::SerializeStatus serializeStatus = buffer.setBuffLen(static_cast(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(readLen)) { this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_DATA_SIZE, static_cast(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(buffSize)); return false; } FW_ASSERT(buffSize >= crcSize, static_cast(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(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(buffLeftSize)); return false; } // Rewind deserialization buffer.resetDeser(); return true; } } // namespace Svc