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