| 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 | // Set the number of records | ||
| 123 | ✗ | this->m_header.m_numRecords = numRecords; | |
| 124 | // Reset deserialization | ||
| 125 | ✗ | this->reset(); | |
| 126 | |||
| 127 | ✗ | return true; | |
| 128 | ✗ | } | |
| 129 | |||
| 130 | ✗ | bool AMPCSSequence ::hasMoreRecords() const { | |
| 131 | ✗ | return this->m_buffer.getDeserializeSizeLeft() > 0; | |
| 132 | } | ||
| 133 | |||
| 134 | ✗ | void AMPCSSequence ::nextRecord(Sequence::Record& record) { | |
| 135 | ✗ | Fw::SerializeStatus status = this->deserializeRecord(record); | |
| 136 | ✗ | FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status); | |
| 137 | ✗ | } | |
| 138 | |||
| 139 | ✗ | void AMPCSSequence ::reset() { | |
| 140 | ✗ | this->m_buffer.resetDeser(); | |
| 141 | ✗ | } | |
| 142 | |||
| 143 | ✗ | void AMPCSSequence ::clear() { | |
| 144 | ✗ | this->m_buffer.resetSer(); | |
| 145 | ✗ | } | |
| 146 | |||
| 147 | ✗ | bool AMPCSSequence ::readCRC() { | |
| 148 | ✗ | Os::File& file = this->m_crcFile; | |
| 149 | ✗ | Fw::LinearBufferBase& buffer = this->m_buffer; | |
| 150 | ✗ | bool status = true; | |
| 151 | Fw::SerializeStatus ser_status; | ||
| 152 | |||
| 153 | ✗ | FwSizeType readLen = static_cast<FwSizeType>(sizeof(U32)); | |
| 154 | ✗ | ser_status = buffer.setBuffLen(static_cast<Fw::Serializable::SizeType>(readLen)); | |
| 155 | ✗ | FW_ASSERT(ser_status == Fw::FW_SERIALIZE_OK, ser_status); | |
| 156 | |||
| 157 | ✗ | U8* const addr = buffer.getBuffAddr(); | |
| 158 | ✗ | Os::File::Status fileStatus = file.read(addr, readLen); | |
| 159 | |||
| 160 | ✗ | if (fileStatus != Os::File::OP_OK) { | |
| 161 | ✗ | this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_CRC, fileStatus); | |
| 162 | ✗ | status = false; | |
| 163 | } | ||
| 164 | |||
| 165 | ✗ | return status; | |
| 166 | } | ||
| 167 | |||
| 168 | ✗ | bool AMPCSSequence ::deserializeCRC() { | |
| 169 | ✗ | bool status = true; | |
| 170 | ✗ | Fw::SerializeStatus serializeStatus = this->m_buffer.deserializeTo(this->m_crc.m_stored); | |
| 171 | ✗ | if (serializeStatus != Fw::FW_SERIALIZE_OK) { | |
| 172 | ✗ | this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_CRC, serializeStatus); | |
| 173 | ✗ | status = false; | |
| 174 | } | ||
| 175 | ✗ | return status; | |
| 176 | } | ||
| 177 | |||
| 178 | ✗ | bool AMPCSSequence ::readOpenSequenceFile() { | |
| 179 | ✗ | this->m_buffer.resetSer(); | |
| 180 | ✗ | this->m_crc.init(); | |
| 181 | ✗ | bool status = this->readSequenceHeader(); | |
| 182 | ✗ | if (status) { | |
| 183 | ✗ | this->m_crc.update(this->m_sequenceHeader, sizeof(this->m_sequenceHeader)); | |
| 184 | ✗ | status = this->readRecords(); | |
| 185 | } | ||
| 186 | ✗ | if (status) { | |
| 187 | ✗ | U8* const buffAddr = this->m_buffer.getBuffAddr(); | |
| 188 | ✗ | const FwSizeType buffLen = this->m_buffer.getSize(); | |
| 189 | ✗ | FW_ASSERT(buffLen == this->m_header.m_fileSize, static_cast<FwAssertArgType>(buffLen), | |
| 190 | static_cast<FwAssertArgType>(this->m_header.m_fileSize)); | ||
| 191 | ✗ | this->m_crc.update(buffAddr, buffLen); | |
| 192 | } | ||
| 193 | ✗ | return status; | |
| 194 | } | ||
| 195 | |||
| 196 | ✗ | bool AMPCSSequence ::readSequenceHeader() { | |
| 197 | ✗ | Os::File& file = this->m_sequenceFile; | |
| 198 | |||
| 199 | ✗ | bool status = true; | |
| 200 | |||
| 201 | ✗ | FwSizeType readLen = sizeof this->m_sequenceHeader; | |
| 202 | ✗ | const Os::File::Status fileStatus = file.read(this->m_sequenceHeader, readLen); | |
| 203 | |||
| 204 | ✗ | if (fileStatus != Os::File::OP_OK) { | |
| 205 | ✗ | this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_HEADER, fileStatus); | |
| 206 | ✗ | status = false; | |
| 207 | } | ||
| 208 | |||
| 209 | ✗ | if (status and readLen != sizeof this->m_sequenceHeader) { | |
| 210 | ✗ | this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_HEADER_SIZE, static_cast<I32>(readLen)); | |
| 211 | ✗ | status = false; | |
| 212 | } | ||
| 213 | |||
| 214 | ✗ | return status; | |
| 215 | } | ||
| 216 | |||
| 217 | ✗ | bool AMPCSSequence ::readRecords() { | |
| 218 | ✗ | Os::File& file = this->m_sequenceFile; | |
| 219 | ✗ | const FwSizeType size = this->m_header.m_fileSize; | |
| 220 | ✗ | Fw::LinearBufferBase& buffer = this->m_buffer; | |
| 221 | ✗ | U8* const addr = buffer.getBuffAddr(); | |
| 222 | |||
| 223 | // Check file size | ||
| 224 | ✗ | if (size > this->m_buffer.getCapacity()) { | |
| 225 | ✗ | this->m_events.fileSizeError(static_cast<U32>(size)); | |
| 226 | ✗ | return false; | |
| 227 | } | ||
| 228 | |||
| 229 | ✗ | FwSizeType readLen = size; | |
| 230 | ✗ | const Os::File::Status fileStatus = file.read(addr, readLen); | |
| 231 | // Check read status | ||
| 232 | ✗ | if (fileStatus != Os::File::OP_OK) { | |
| 233 | ✗ | this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_DATA, fileStatus); | |
| 234 | ✗ | return false; | |
| 235 | } | ||
| 236 | // Check read size | ||
| 237 | ✗ | const FwSizeType readLenUint = static_cast<FwSizeType>(readLen); | |
| 238 | ✗ | if (readLenUint != size) { | |
| 239 | ✗ | this->m_events.fileInvalid(CmdSequencer_FileReadStage::READ_SEQ_DATA_SIZE, static_cast<I32>(readLen)); | |
| 240 | ✗ | return false; | |
| 241 | } | ||
| 242 | // set buffer size | ||
| 243 | ✗ | const Fw::SerializeStatus serializeStatus = buffer.setBuffLen(size); | |
| 244 | ✗ | FW_ASSERT(serializeStatus == Fw::FW_SERIALIZE_OK, serializeStatus); | |
| 245 | ✗ | return true; | |
| 246 | } | ||
| 247 | |||
| 248 | ✗ | Fw::SerializeStatus AMPCSSequence ::deserializeRecord(Sequence::Record& record) { | |
| 249 | Record::CmdLength::t cmdLength; | ||
| 250 | |||
| 251 | ✗ | Fw::SerializeStatus status = this->deserializeTimeFlag(record.m_descriptor); | |
| 252 | |||
| 253 | ✗ | if (status == Fw::FW_SERIALIZE_OK) { | |
| 254 | ✗ | status = this->deserializeTime(record.m_timeTag); | |
| 255 | } | ||
| 256 | ✗ | if (status == Fw::FW_SERIALIZE_OK) { | |
| 257 | ✗ | status = this->deserializeCmdLength(cmdLength); | |
| 258 | } | ||
| 259 | ✗ | if (status == Fw::FW_SERIALIZE_OK) { | |
| 260 | ✗ | status = this->translateCommand(record.m_command, cmdLength); | |
| 261 | } | ||
| 262 | |||
| 263 | ✗ | return status; | |
| 264 | } | ||
| 265 | |||
| 266 | ✗ | Fw::SerializeStatus AMPCSSequence ::deserializeTimeFlag(Sequence::Record::Descriptor& descriptor) { | |
| 267 | ✗ | Fw::LinearBufferBase& buffer = this->m_buffer; | |
| 268 | Record::TimeFlag::Serial::t timeFlagSerial; | ||
| 269 | ✗ | Fw::SerializeStatus status = buffer.deserializeTo(timeFlagSerial); | |
| 270 | ✗ | if (status == Fw::FW_SERIALIZE_OK) { | |
| 271 | ✗ | switch (timeFlagSerial) { | |
| 272 | ✗ | case Record::TimeFlag::ABSOLUTE: | |
| 273 | ✗ | descriptor = Sequence::Record::ABSOLUTE; | |
| 274 | ✗ | break; | |
| 275 | ✗ | case Record::TimeFlag::RELATIVE: | |
| 276 | ✗ | descriptor = Sequence::Record::RELATIVE; | |
| 277 | ✗ | break; | |
| 278 | ✗ | default: | |
| 279 | ✗ | status = Fw::FW_DESERIALIZE_FORMAT_ERROR; | |
| 280 | ✗ | break; | |
| 281 | } | ||
| 282 | } | ||
| 283 | ✗ | return status; | |
| 284 | } | ||
| 285 | |||
| 286 | ✗ | Fw::SerializeStatus AMPCSSequence ::deserializeTime(Fw::Time& timeTag) { | |
| 287 | Record::Time::t time; | ||
| 288 | ✗ | Fw::LinearBufferBase& buffer = this->m_buffer; | |
| 289 | ✗ | Fw::SerializeStatus status = buffer.deserializeTo(time); | |
| 290 | ✗ | if (status == Fw::FW_SERIALIZE_OK) { | |
| 291 | ✗ | timeTag.set(time, 0); | |
| 292 | } | ||
| 293 | ✗ | return status; | |
| 294 | } | ||
| 295 | |||
| 296 | ✗ | Fw::SerializeStatus AMPCSSequence ::deserializeCmdLength(Record::CmdLength::t& cmdLength) { | |
| 297 | ✗ | Fw::LinearBufferBase& buffer = this->m_buffer; | |
| 298 | ✗ | Fw::SerializeStatus status = buffer.deserializeTo(cmdLength); | |
| 299 | ✗ | if (status == Fw::FW_SERIALIZE_OK and cmdLength > buffer.getDeserializeSizeLeft()) { | |
| 300 | // Not enough data left | ||
| 301 | ✗ | status = Fw::FW_DESERIALIZE_SIZE_MISMATCH; | |
| 302 | } | ||
| 303 | // The translated record occupies the packet descriptor, the zero-extension bytes prepended to | ||
| 304 | // the two-byte AMPCS opcode, and the record body. It must fit the ComBuffer capacity: | ||
| 305 | // SERIALIZED_SIZE is the capacity plus the stored length, and comparing against it admits | ||
| 306 | // records that setBuffLen then rejects, asserting in translateCommand | ||
| 307 | ✗ | const FwSizeType translatedSize = | |
| 308 | ✗ | sizeof(FwPacketDescriptorType) + (sizeof(FwOpcodeType) - 2) + static_cast<FwSizeType>(cmdLength); | |
| 309 | ✗ | if (status == Fw::FW_SERIALIZE_OK and translatedSize > FW_COM_BUFFER_MAX_SIZE) { | |
| 310 | // Record size is too big for com buffer | ||
| 311 | ✗ | status = Fw::FW_DESERIALIZE_SIZE_MISMATCH; | |
| 312 | } | ||
| 313 | ✗ | return status; | |
| 314 | } | ||
| 315 | |||
| 316 | ✗ | Fw::SerializeStatus AMPCSSequence ::translateCommand(Fw::ComBuffer& comBuffer, const Record::CmdLength::t cmdLength) { | |
| 317 | ✗ | Fw::LinearBufferBase& buffer = this->m_buffer; | |
| 318 | ✗ | comBuffer.resetSer(); | |
| 319 | // Serialize the command packet descriptor | ||
| 320 | ✗ | const FwPacketDescriptorType cmdDescriptor = Fw::ComPacketType::FW_PACKET_COMMAND; | |
| 321 | ✗ | Fw::SerializeStatus status = comBuffer.serializeFrom(cmdDescriptor); | |
| 322 | ✗ | FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status); | |
| 323 | // Zero-extend the two-byte AMPCS opcode by (sizeof(FwOpcodeType) - 2) bytes | ||
| 324 | static_assert(sizeof(FwOpcodeType) >= 2, "FwOpcodeType must be at least two bytes wide"); | ||
| 325 | ✗ | U32 sizeOfZeros = 0; | |
| 326 | ✗ | const FwIndexType bytesToExtend = sizeof(FwOpcodeType) - 2; | |
| 327 | ✗ | const U8 zeros = 0; | |
| 328 | ✗ | for (FwIndexType i = 0; i < bytesToExtend; i++) { | |
| 329 | ✗ | status = comBuffer.serializeFrom(zeros); | |
| 330 | ✗ | FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status); | |
| 331 | ✗ | sizeOfZeros += static_cast<U32>(sizeof(zeros)); | |
| 332 | } | ||
| 333 | // Set the buffer length | ||
| 334 | ✗ | const U32 fixedBuffLen = static_cast<U32>(comBuffer.getSize()); | |
| 335 | ✗ | FW_ASSERT(fixedBuffLen == sizeof(cmdDescriptor) + sizeOfZeros, static_cast<FwAssertArgType>(fixedBuffLen)); | |
| 336 | ✗ | const U32 totalBuffLen = fixedBuffLen + cmdLength; | |
| 337 | ✗ | status = comBuffer.setBuffLen(totalBuffLen); | |
| 338 | ✗ | FW_ASSERT(status == Fw::FW_SERIALIZE_OK, status); | |
| 339 | // Copy the opcode and argument bytes | ||
| 340 | ✗ | FwSizeType size = cmdLength; | |
| 341 | ✗ | U8* const addr = comBuffer.getBuffAddr(); | |
| 342 | ✗ | FW_ASSERT(addr != nullptr); | |
| 343 | // true means "don't serialize the length" | ||
| 344 | ✗ | status = buffer.deserializeTo(&addr[fixedBuffLen], size, Fw::Serialization::OMIT_LENGTH); | |
| 345 | ✗ | return status; | |
| 346 | } | ||
| 347 | |||
| 348 | } // namespace Svc | ||
| 349 |