| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title FileDownlink.hpp | ||
| 3 | // \author bocchino, mstarch | ||
| 4 | // \brief hpp file for FileDownlink component implementation class | ||
| 5 | // | ||
| 6 | // \copyright | ||
| 7 | // Copyright 2009-2015, by the California Institute of Technology. | ||
| 8 | // ALL RIGHTS RESERVED. United States Government Sponsorship | ||
| 9 | // acknowledged. | ||
| 10 | // ====================================================================== | ||
| 11 | |||
| 12 | #include <Fw/Com/ComPacket.hpp> | ||
| 13 | #include <Fw/FPrimeBasicTypes.hpp> | ||
| 14 | #include <Fw/Types/Assert.hpp> | ||
| 15 | #include <Fw/Types/StringUtils.hpp> | ||
| 16 | #include <Os/QueueString.hpp> | ||
| 17 | #include <Svc/FileDownlink/FileDownlink.hpp> | ||
| 18 | #include <limits> | ||
| 19 | |||
| 20 | namespace Svc { | ||
| 21 | |||
| 22 | // ---------------------------------------------------------------------- | ||
| 23 | // Construction, initialization, and destruction | ||
| 24 | // ---------------------------------------------------------------------- | ||
| 25 | |||
| 26 | 1 | FileDownlink ::FileDownlink(const char* const name) | |
| 27 | : FileDownlinkComponentBase(name), | ||
| 28 | 1 | m_configured(false), | |
| 29 | 1 | m_filesSent(this), | |
| 30 | 1 | m_packetsSent(this), | |
| 31 | 1 | m_warnings(this), | |
| 32 | 1 | m_sequenceIndex(0), | |
| 33 | 1 | m_curTimer(0), | |
| 34 | 1 | m_bufferSize(0), | |
| 35 | 1 | m_byteOffset(0), | |
| 36 | 1 | m_endOffset(0), | |
| 37 | 1 | m_lastCompletedType(Fw::FilePacket::T_NONE), | |
| 38 | 1 | m_lastBufferId(0), | |
| 39 |
1/1✓ Branch 1 taken 1 times.
|
1 | m_curEntry(), |
| 40 |
4/4✓ Branch 2 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 11 taken 1 times.
|
3 | m_cntxId(0) {} |
| 41 | |||
| 42 | 1 | void FileDownlink ::configure(U32 cooldown, U32 cycleTime, U32 fileQueueDepth) { | |
| 43 | 1 | this->m_cooldown = cooldown; | |
| 44 | 1 | this->m_cycleTime = cycleTime; | |
| 45 | 1 | this->m_configured = true; | |
| 46 | |||
| 47 | Os::Queue::Status stat = | ||
| 48 |
3/3✓ Branch 1 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 7 taken 1 times.
|
1 | m_fileQueue.create(this->getInstance(), Os::QueueString("fileDownlinkQueue"), |
| 49 | static_cast<FwSizeType>(fileQueueDepth), static_cast<FwSizeType>(sizeof(struct FileEntry))); | ||
| 50 | 1 | FW_ASSERT(stat == Os::Queue::OP_OK, static_cast<FwAssertArgType>(stat)); | |
| 51 | 1 | } | |
| 52 | |||
| 53 | ✗ | void FileDownlink ::configure(const char* directory) { | |
| 54 | ✗ | this->m_file.configureSandbox(directory); | |
| 55 | ✗ | } | |
| 56 | |||
| 57 | 1 | void FileDownlink ::deinit() { | |
| 58 | 1 | this->m_fileQueue.teardown(); | |
| 59 | 1 | FileDownlinkComponentBase::deinit(); | |
| 60 | 1 | } | |
| 61 | |||
| 62 | 1 | void FileDownlink ::preamble() { | |
| 63 | 1 | FW_ASSERT(this->m_configured == true); | |
| 64 | 1 | } | |
| 65 | |||
| 66 | 2 | FileDownlink ::~FileDownlink() {} | |
| 67 | |||
| 68 | // ---------------------------------------------------------------------- | ||
| 69 | // Handler implementations for user-defined typed input ports | ||
| 70 | // ---------------------------------------------------------------------- | ||
| 71 | |||
| 72 | 235 | void FileDownlink ::Run_handler(const FwIndexType portNum, U32 context) { | |
| 73 |
1/4✓ Branch 1 taken 235 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
235 | switch (this->m_mode.get()) { |
| 74 | 235 | case Mode::IDLE: { | |
| 75 | 235 | FwSizeType real_size = 0; | |
| 76 | 235 | FwQueuePriorityType prio = 0; | |
| 77 |
1/1✓ Branch 1 taken 235 times.
|
235 | Os::Queue::Status stat = m_fileQueue.receive(reinterpret_cast<U8*>(&this->m_curEntry), |
| 78 | static_cast<FwSizeType>(sizeof(this->m_curEntry)), | ||
| 79 | Os::Queue::BlockingType::NONBLOCKING, real_size, prio); | ||
| 80 | |||
| 81 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 235 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
235 | if (stat != Os::Queue::Status::OP_OK || sizeof(this->m_curEntry) != real_size) { |
| 82 | 235 | return; | |
| 83 | } | ||
| 84 | |||
| 85 | ✗ | sendFile(this->m_curEntry.srcFilename, this->m_curEntry.destFilename, this->m_curEntry.offset, | |
| 86 | this->m_curEntry.length); | ||
| 87 | ✗ | break; | |
| 88 | } | ||
| 89 | ✗ | case Mode::COOLDOWN: { | |
| 90 | ✗ | if (this->m_curTimer >= this->m_cooldown) { | |
| 91 | ✗ | this->m_curTimer = 0; | |
| 92 | ✗ | this->m_mode.set(Mode::IDLE); | |
| 93 | } else { | ||
| 94 | ✗ | this->m_curTimer += m_cycleTime; | |
| 95 | } | ||
| 96 | ✗ | break; | |
| 97 | } | ||
| 98 | ✗ | case Mode::WAIT: { | |
| 99 | ✗ | this->m_curTimer += m_cycleTime; | |
| 100 | ✗ | break; | |
| 101 | } | ||
| 102 | ✗ | default: | |
| 103 | ✗ | break; | |
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | ✗ | Svc::SendFileResponse FileDownlink ::SendFile_handler( | |
| 108 | const FwIndexType portNum, | ||
| 109 | const Fw::StringBase& sourceFilename, // lgtm[cpp/large-parameter] dictated by command architecture | ||
| 110 | const Fw::StringBase& destFilename, // lgtm[cpp/large-parameter] dictated by command architecture | ||
| 111 | U32 offset, | ||
| 112 | U32 length) { | ||
| 113 | ✗ | struct FileEntry entry; | |
| 114 | ✗ | entry.offset = offset; | |
| 115 | ✗ | entry.length = length; | |
| 116 | ✗ | entry.source = FileDownlink::PORT; | |
| 117 | ✗ | entry.opCode = 0; | |
| 118 | ✗ | entry.cmdSeq = 0; | |
| 119 | ✗ | entry.context = m_cntxId++; | |
| 120 | |||
| 121 | // Guard against filename overflow | ||
| 122 | ✗ | if (sourceFilename.length() >= entry.srcFilename.getCapacity()) { | |
| 123 | ✗ | this->log_WARNING_HI_FilenameSourceOverflow(); | |
| 124 | ✗ | return SendFileResponse(SendFileStatus::STATUS_ERROR, std::numeric_limits<U32>::max()); | |
| 125 | ✗ | } else if (destFilename.length() >= entry.destFilename.getCapacity()) { | |
| 126 | ✗ | this->log_WARNING_HI_FilenameDestinationOverflow(); | |
| 127 | ✗ | return SendFileResponse(SendFileStatus::STATUS_ERROR, std::numeric_limits<U32>::max()); | |
| 128 | } else { | ||
| 129 | ✗ | entry.srcFilename = sourceFilename; | |
| 130 | ✗ | entry.destFilename = destFilename; | |
| 131 | |||
| 132 | Os::Queue::Status status = | ||
| 133 | ✗ | m_fileQueue.send(reinterpret_cast<U8*>(&entry), static_cast<FwSizeType>(sizeof(entry)), 0, | |
| 134 | Os::Queue::BlockingType::NONBLOCKING); | ||
| 135 | |||
| 136 | ✗ | if (status != Os::Queue::Status::OP_OK) { | |
| 137 | ✗ | return SendFileResponse(SendFileStatus::STATUS_ERROR, std::numeric_limits<U32>::max()); | |
| 138 | } | ||
| 139 | ✗ | return SendFileResponse(SendFileStatus::STATUS_OK, entry.context); | |
| 140 | } | ||
| 141 | ✗ | } | |
| 142 | |||
| 143 | 59 | void FileDownlink ::pingIn_handler(const FwIndexType portNum, U32 key) { | |
| 144 | 59 | this->pingOut_out(0, key); | |
| 145 | 59 | } | |
| 146 | |||
| 147 | ✗ | void FileDownlink ::bufferReturn_handler(const FwIndexType portNum, Fw::Buffer& fwBuffer) { | |
| 148 | // If this is a stale buffer (old, timed-out, or both), then ignore its return. | ||
| 149 | // File downlink actions only respond to the return of the most-recently-sent buffer. | ||
| 150 | ✗ | if (this->m_lastBufferId != fwBuffer.getContext() + 1 || this->m_mode.get() == Mode::IDLE) { | |
| 151 | ✗ | return; | |
| 152 | } | ||
| 153 | // Non-ignored buffers cannot be returned in "DOWNLINK" and "IDLE" state. Only in "WAIT", "CANCEL" state. | ||
| 154 | ✗ | FW_ASSERT(this->m_mode.get() == Mode::WAIT || this->m_mode.get() == Mode::CANCEL, | |
| 155 | static_cast<FwAssertArgType>(this->m_mode.get())); | ||
| 156 | // If the last packet has been sent (and is returning now) then finish the file | ||
| 157 | ✗ | if (this->m_lastCompletedType == Fw::FilePacket::T_END || this->m_lastCompletedType == Fw::FilePacket::T_CANCEL) { | |
| 158 | ✗ | finishHelper(this->m_lastCompletedType == Fw::FilePacket::T_CANCEL); | |
| 159 | ✗ | return; | |
| 160 | } | ||
| 161 | // If waiting and a buffer is in-bound, then switch to downlink mode | ||
| 162 | ✗ | else if (this->m_mode.get() == Mode::WAIT) { | |
| 163 | ✗ | this->m_mode.set(Mode::DOWNLINK); | |
| 164 | } | ||
| 165 | |||
| 166 | ✗ | this->downlinkPacket(); | |
| 167 | } | ||
| 168 | |||
| 169 | // ---------------------------------------------------------------------- | ||
| 170 | // Command handler implementations | ||
| 171 | // ---------------------------------------------------------------------- | ||
| 172 | |||
| 173 | ✗ | void FileDownlink ::SendFile_cmdHandler(const FwOpcodeType opCode, | |
| 174 | const U32 cmdSeq, | ||
| 175 | const Fw::CmdStringArg& sourceFilename, | ||
| 176 | const Fw::CmdStringArg& destFilename) { | ||
| 177 | ✗ | struct FileEntry entry; | |
| 178 | ✗ | entry.offset = 0; | |
| 179 | ✗ | entry.length = 0; | |
| 180 | ✗ | entry.source = FileDownlink::COMMAND; | |
| 181 | ✗ | entry.opCode = opCode; | |
| 182 | ✗ | entry.cmdSeq = cmdSeq; | |
| 183 | ✗ | entry.context = std::numeric_limits<U32>::max(); | |
| 184 | |||
| 185 | // Guard against filename overflow | ||
| 186 | ✗ | if (sourceFilename.length() >= entry.srcFilename.getCapacity()) { | |
| 187 | ✗ | this->log_WARNING_HI_FilenameSourceOverflow(); | |
| 188 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); | |
| 189 | ✗ | } else if (destFilename.length() >= entry.destFilename.getCapacity()) { | |
| 190 | ✗ | this->log_WARNING_HI_FilenameDestinationOverflow(); | |
| 191 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); | |
| 192 | } else { | ||
| 193 | ✗ | entry.srcFilename = sourceFilename; | |
| 194 | ✗ | entry.destFilename = destFilename; | |
| 195 | |||
| 196 | Os::Queue::Status status = | ||
| 197 | ✗ | m_fileQueue.send(reinterpret_cast<U8*>(&entry), static_cast<FwSizeType>(sizeof(entry)), 0, | |
| 198 | Os::Queue::BlockingType::NONBLOCKING); | ||
| 199 | |||
| 200 | ✗ | if (status != Os::Queue::Status::OP_OK) { | |
| 201 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); | |
| 202 | } | ||
| 203 | } | ||
| 204 | ✗ | } | |
| 205 | |||
| 206 | ✗ | void FileDownlink ::SendPartial_cmdHandler(FwOpcodeType opCode, | |
| 207 | U32 cmdSeq, | ||
| 208 | const Fw::CmdStringArg& sourceFilename, | ||
| 209 | const Fw::CmdStringArg& destFilename, | ||
| 210 | U32 startOffset, | ||
| 211 | U32 length) { | ||
| 212 | ✗ | struct FileEntry entry; | |
| 213 | ✗ | entry.offset = startOffset; | |
| 214 | ✗ | entry.length = length; | |
| 215 | ✗ | entry.source = FileDownlink::COMMAND; | |
| 216 | ✗ | entry.opCode = opCode; | |
| 217 | ✗ | entry.cmdSeq = cmdSeq; | |
| 218 | ✗ | entry.context = std::numeric_limits<U32>::max(); | |
| 219 | |||
| 220 | // Guard against filename overflow | ||
| 221 | ✗ | if (sourceFilename.length() >= entry.srcFilename.getCapacity()) { | |
| 222 | ✗ | this->log_WARNING_HI_FilenameSourceOverflow(); | |
| 223 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); | |
| 224 | ✗ | } else if (destFilename.length() >= entry.destFilename.getCapacity()) { | |
| 225 | ✗ | this->log_WARNING_HI_FilenameDestinationOverflow(); | |
| 226 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); | |
| 227 | } else { | ||
| 228 | ✗ | entry.srcFilename = sourceFilename; | |
| 229 | ✗ | entry.destFilename = destFilename; | |
| 230 | |||
| 231 | Os::Queue::Status status = | ||
| 232 | ✗ | m_fileQueue.send(reinterpret_cast<U8*>(&entry), static_cast<FwSizeType>(sizeof(entry)), 0, | |
| 233 | Os::Queue::BlockingType::NONBLOCKING); | ||
| 234 | |||
| 235 | ✗ | if (status != Os::Queue::Status::OP_OK) { | |
| 236 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); | |
| 237 | } | ||
| 238 | } | ||
| 239 | ✗ | } | |
| 240 | |||
| 241 | ✗ | void FileDownlink ::Cancel_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq) { | |
| 242 | // Must be able to cancel in both downlink and waiting states | ||
| 243 | ✗ | if (this->m_mode.get() == Mode::DOWNLINK || this->m_mode.get() == Mode::WAIT) { | |
| 244 | ✗ | this->m_mode.set(Mode::CANCEL); | |
| 245 | } | ||
| 246 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); | |
| 247 | ✗ | } | |
| 248 | |||
| 249 | // ---------------------------------------------------------------------- | ||
| 250 | // Private helper methods | ||
| 251 | // ---------------------------------------------------------------------- | ||
| 252 | |||
| 253 | ✗ | Fw::CmdResponse FileDownlink ::statusToCmdResp(SendFileStatus status) { | |
| 254 | ✗ | switch (status.e) { | |
| 255 | ✗ | case SendFileStatus::STATUS_OK: | |
| 256 | ✗ | return Fw::CmdResponse::OK; | |
| 257 | ✗ | case SendFileStatus::STATUS_ERROR: | |
| 258 | ✗ | return Fw::CmdResponse::EXECUTION_ERROR; | |
| 259 | ✗ | case SendFileStatus::STATUS_INVALID: | |
| 260 | ✗ | return Fw::CmdResponse::VALIDATION_ERROR; | |
| 261 | ✗ | case SendFileStatus::STATUS_BUSY: | |
| 262 | ✗ | return Fw::CmdResponse::BUSY; | |
| 263 | ✗ | default: | |
| 264 | // Trigger assertion if given unknown status | ||
| 265 | ✗ | FW_ASSERT(false); | |
| 266 | } | ||
| 267 | |||
| 268 | // It's impossible to reach this, but added to suppress gcc missing return warning | ||
| 269 | ✗ | return Fw::CmdResponse::EXECUTION_ERROR; | |
| 270 | } | ||
| 271 | |||
| 272 | ✗ | void FileDownlink ::sendResponse(SendFileStatus resp) { | |
| 273 | ✗ | FW_ASSERT(this->m_curEntry.source == FileDownlink::COMMAND || this->m_curEntry.source == FileDownlink::PORT, | |
| 274 | static_cast<FwAssertArgType>(this->m_curEntry.source)); | ||
| 275 | ✗ | if (this->m_curEntry.source == FileDownlink::COMMAND) { | |
| 276 | ✗ | this->cmdResponse_out(this->m_curEntry.opCode, this->m_curEntry.cmdSeq, statusToCmdResp(resp)); | |
| 277 | } else { | ||
| 278 | ✗ | for (FwIndexType i = 0; i < this->getNum_FileComplete_OutputPorts(); i++) { | |
| 279 | ✗ | if (this->isConnected_FileComplete_OutputPort(i)) { | |
| 280 | ✗ | this->FileComplete_out(i, Svc::SendFileResponse(resp, this->m_curEntry.context)); | |
| 281 | } | ||
| 282 | } | ||
| 283 | } | ||
| 284 | ✗ | } | |
| 285 | |||
| 286 | ✗ | void FileDownlink ::sendFile(const Fw::FileNameString& sourceFilename, | |
| 287 | const Fw::FileNameString& destFilename, | ||
| 288 | U32 startOffset, | ||
| 289 | U32 length) { | ||
| 290 | // Open file for downlink | ||
| 291 | ✗ | Os::File::Status status = this->m_file.open(sourceFilename, destFilename); | |
| 292 | |||
| 293 | // Reject command if error when opening file | ||
| 294 | ✗ | if (status != Os::File::OP_OK) { | |
| 295 | ✗ | this->m_mode.set(Mode::IDLE); | |
| 296 | ✗ | if (status == Os::File::OUTSIDE_SANDBOX) { | |
| 297 | ✗ | this->m_warnings.sourceOutOfSandbox(); | |
| 298 | } else { | ||
| 299 | ✗ | this->m_warnings.fileOpenError(); | |
| 300 | } | ||
| 301 | ✗ | sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK : SendFileStatus::STATUS_ERROR); | |
| 302 | ✗ | return; | |
| 303 | } | ||
| 304 | ✗ | const U32 fileSize = this->m_file.getSize(); | |
| 305 | |||
| 306 | ✗ | if (fileSize == 0) { | |
| 307 | ✗ | this->m_file.getOsFile().close(); | |
| 308 | ✗ | this->m_mode.set(Mode::IDLE); | |
| 309 | ✗ | this->m_warnings.zeroSize(); | |
| 310 | ✗ | sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK | |
| 311 | : SendFileStatus::STATUS_INVALID); | ||
| 312 | ✗ | return; | |
| 313 | |||
| 314 | ✗ | } else if (startOffset >= fileSize) { | |
| 315 | ✗ | this->enterCooldown(); | |
| 316 | ✗ | this->log_WARNING_HI_DownlinkPartialFail(this->m_file.getSourceName(), this->m_file.getDestName(), startOffset, | |
| 317 | fileSize); | ||
| 318 | ✗ | sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK | |
| 319 | : SendFileStatus::STATUS_INVALID); | ||
| 320 | ✗ | return; | |
| 321 | // startOffset < fileSize here, so the subtraction cannot underflow and, unlike | ||
| 322 | // startOffset + length, cannot wrap | ||
| 323 | ✗ | } else if (length > fileSize - startOffset) { | |
| 324 | // If the amount to downlink is greater than the file size, emit a Warning and then allow | ||
| 325 | // the file to be downlinked anyway | ||
| 326 | ✗ | this->log_WARNING_LO_DownlinkPartialWarning(startOffset, length, fileSize, this->m_file.getSourceName(), | |
| 327 | ✗ | this->m_file.getDestName()); | |
| 328 | ✗ | length = fileSize - startOffset; | |
| 329 | } | ||
| 330 | |||
| 331 | // Send file and switch to WAIT mode | ||
| 332 | ✗ | this->getBuffer(this->m_buffer, FILE_PACKET); | |
| 333 | ✗ | this->sendStartPacket(); | |
| 334 | ✗ | this->m_mode.set(Mode::WAIT); | |
| 335 | ✗ | this->m_sequenceIndex = 1; | |
| 336 | ✗ | this->m_curTimer = 0; | |
| 337 | ✗ | this->m_byteOffset = startOffset; | |
| 338 | ✗ | this->m_lastCompletedType = Fw::FilePacket::T_START; | |
| 339 | |||
| 340 | // zero length means read until end of file | ||
| 341 | ✗ | if (length > 0) { | |
| 342 | ✗ | this->log_ACTIVITY_HI_SendStarted(length, this->m_file.getSourceName(), this->m_file.getDestName()); | |
| 343 | ✗ | this->m_endOffset = startOffset + length; | |
| 344 | } else { | ||
| 345 | ✗ | this->log_ACTIVITY_HI_SendStarted(fileSize - startOffset, this->m_file.getSourceName(), | |
| 346 | ✗ | this->m_file.getDestName()); | |
| 347 | ✗ | this->m_endOffset = fileSize; | |
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | ✗ | Os::File::Status FileDownlink ::sendDataPacket(U32& byteOffset) { | |
| 352 | ✗ | const U32 maxDataSize = | |
| 353 | FILEDOWNLINK_INTERNAL_BUFFER_SIZE - Fw::FilePacket::DataPacket::HEADERSIZE - sizeof(FwPacketDescriptorType); | ||
| 354 | // The caller is required to maintain byteOffset < m_endOffset. | ||
| 355 | ✗ | if (byteOffset >= this->m_endOffset) { | |
| 356 | ✗ | return Os::File::INVALID_ARGUMENT; | |
| 357 | } | ||
| 358 | // Subtraction cannot underflow given the check above; comparing the remainder against | ||
| 359 | // maxDataSize (rather than byteOffset + maxDataSize against m_endOffset) also avoids an | ||
| 360 | // addition that could wrap when byteOffset is near the top of the U32 range. dataSize is | ||
| 361 | // therefore always <= maxDataSize, i.e. never larger than the buffer below. | ||
| 362 | ✗ | const U32 remaining = this->m_endOffset - byteOffset; | |
| 363 | ✗ | const U32 dataSize = (remaining < maxDataSize) ? remaining : maxDataSize; | |
| 364 | U8 buffer[maxDataSize]; | ||
| 365 | // This will be last data packet sent | ||
| 366 | ✗ | if (dataSize + byteOffset == this->m_endOffset) { | |
| 367 | ✗ | this->m_lastCompletedType = Fw::FilePacket::T_DATA; | |
| 368 | } | ||
| 369 | |||
| 370 | ✗ | const Os::File::Status status = this->m_file.read(buffer, byteOffset, dataSize); | |
| 371 | ✗ | if (status != Os::File::OP_OK) { | |
| 372 | ✗ | this->m_warnings.fileRead(status); | |
| 373 | ✗ | return status; | |
| 374 | } | ||
| 375 | |||
| 376 | Fw::FilePacket::DataPacket dataPacket; | ||
| 377 | ✗ | dataPacket.initialize(this->m_sequenceIndex, byteOffset, static_cast<U16>(dataSize), buffer); | |
| 378 | ✗ | ++this->m_sequenceIndex; | |
| 379 | ✗ | Fw::FilePacket filePacket; | |
| 380 | ✗ | filePacket.fromDataPacket(dataPacket); | |
| 381 | ✗ | this->sendFilePacket(filePacket); | |
| 382 | |||
| 383 | ✗ | byteOffset += dataSize; | |
| 384 | |||
| 385 | ✗ | return Os::File::OP_OK; | |
| 386 | } | ||
| 387 | |||
| 388 | ✗ | void FileDownlink ::sendCancelPacket() { | |
| 389 | ✗ | Fw::Buffer buffer; | |
| 390 | Fw::FilePacket::CancelPacket cancelPacket; | ||
| 391 | ✗ | cancelPacket.initialize(this->m_sequenceIndex); | |
| 392 | |||
| 393 | ✗ | Fw::FilePacket filePacket; | |
| 394 | ✗ | filePacket.fromCancelPacket(cancelPacket); | |
| 395 | ✗ | this->getBuffer(buffer, CANCEL_PACKET); | |
| 396 | ✗ | FW_ASSERT(buffer.getSize() >= filePacket.bufferSize() + sizeof(FwPacketDescriptorType), | |
| 397 | static_cast<FwAssertArgType>(buffer.getSize()), | ||
| 398 | static_cast<FwAssertArgType>(filePacket.bufferSize() + sizeof(FwPacketDescriptorType))); | ||
| 399 | |||
| 400 | // Serialize the packet descriptor FW_PACKET_FILE to the buffer | ||
| 401 | Fw::SerializeStatus status = | ||
| 402 | ✗ | buffer.getSerializer().serializeFrom(static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_FILE)); | |
| 403 | ✗ | FW_ASSERT(status == Fw::FW_SERIALIZE_OK); | |
| 404 | ✗ | Fw::Buffer offsetBuffer(buffer.getData() + sizeof(FwPacketDescriptorType), | |
| 405 | ✗ | buffer.getSize() - static_cast<Fw::Buffer::SizeType>(sizeof(FwPacketDescriptorType))); | |
| 406 | // Serialize the filePacket content into the buffer | ||
| 407 | ✗ | status = filePacket.toBuffer(offsetBuffer); | |
| 408 | ✗ | FW_ASSERT(status == Fw::FW_SERIALIZE_OK); | |
| 409 | ✗ | const U32 bufferSize = filePacket.bufferSize() + static_cast<U32>(sizeof(FwPacketDescriptorType)); | |
| 410 | ✗ | FW_ASSERT(buffer.getSize() >= bufferSize, static_cast<FwAssertArgType>(buffer.getSize()), | |
| 411 | static_cast<FwAssertArgType>(bufferSize)); | ||
| 412 | ✗ | buffer.setSize(bufferSize); | |
| 413 | ✗ | this->bufferSendOut_out(0, buffer); | |
| 414 | ✗ | this->m_packetsSent.packetSent(); | |
| 415 | ✗ | } | |
| 416 | |||
| 417 | ✗ | void FileDownlink ::sendEndPacket() { | |
| 418 | ✗ | CFDP::Checksum checksum; | |
| 419 | ✗ | this->m_file.getChecksum(checksum); | |
| 420 | |||
| 421 | Fw::FilePacket::EndPacket endPacket; | ||
| 422 | ✗ | endPacket.initialize(this->m_sequenceIndex, checksum); | |
| 423 | |||
| 424 | ✗ | Fw::FilePacket filePacket; | |
| 425 | ✗ | filePacket.fromEndPacket(endPacket); | |
| 426 | ✗ | this->sendFilePacket(filePacket); | |
| 427 | ✗ | } | |
| 428 | |||
| 429 | ✗ | void FileDownlink ::sendStartPacket() { | |
| 430 | Fw::FilePacket::StartPacket startPacket; | ||
| 431 | ✗ | startPacket.initialize(this->m_file.getSize(), this->m_file.getSourceName().toChar(), | |
| 432 | ✗ | this->m_file.getDestName().toChar()); | |
| 433 | ✗ | Fw::FilePacket filePacket; | |
| 434 | ✗ | filePacket.fromStartPacket(startPacket); | |
| 435 | ✗ | this->sendFilePacket(filePacket); | |
| 436 | ✗ | } | |
| 437 | |||
| 438 | ✗ | void FileDownlink ::sendFilePacket(const Fw::FilePacket& filePacket) { | |
| 439 | ✗ | const U32 bufferSize = filePacket.bufferSize() + static_cast<U32>(sizeof(FwPacketDescriptorType)); | |
| 440 | ✗ | FW_ASSERT(this->m_buffer.getData() != nullptr); | |
| 441 | ✗ | FW_ASSERT(this->m_buffer.getSize() >= bufferSize, static_cast<FwAssertArgType>(bufferSize), | |
| 442 | static_cast<FwAssertArgType>(this->m_buffer.getSize())); | ||
| 443 | // Serialize packet descriptor FW_PACKET_FILE to the buffer | ||
| 444 | ✗ | Fw::SerializeStatus status = this->m_buffer.getSerializer().serializeFrom( | |
| 445 | static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_FILE)); | ||
| 446 | ✗ | FW_ASSERT(status == Fw::FW_SERIALIZE_OK); | |
| 447 | // Serialize the filePacket content into the buffer, offset by the size of the packet descriptor | ||
| 448 | Fw::Buffer offsetBuffer( | ||
| 449 | ✗ | this->m_buffer.getData() + sizeof(FwPacketDescriptorType), | |
| 450 | ✗ | this->m_buffer.getSize() - static_cast<Fw::Buffer::SizeType>(sizeof(FwPacketDescriptorType))); | |
| 451 | ✗ | status = filePacket.toBuffer(offsetBuffer); | |
| 452 | ✗ | FW_ASSERT(status == Fw::FW_SERIALIZE_OK); | |
| 453 | // set the buffer size to the packet size | ||
| 454 | ✗ | this->m_buffer.setSize(bufferSize); | |
| 455 | ✗ | this->bufferSendOut_out(0, this->m_buffer); | |
| 456 | // restore buffer size to max | ||
| 457 | ✗ | this->m_buffer.setSize(FILEDOWNLINK_INTERNAL_BUFFER_SIZE); | |
| 458 | ✗ | this->m_packetsSent.packetSent(); | |
| 459 | ✗ | } | |
| 460 | |||
| 461 | ✗ | void FileDownlink ::enterCooldown() { | |
| 462 | ✗ | this->m_file.getOsFile().close(); | |
| 463 | ✗ | this->m_mode.set(Mode::COOLDOWN); | |
| 464 | ✗ | this->m_lastCompletedType = Fw::FilePacket::T_NONE; | |
| 465 | ✗ | this->m_curTimer = 0; | |
| 466 | ✗ | } | |
| 467 | |||
| 468 | ✗ | void FileDownlink ::downlinkPacket() { | |
| 469 | ✗ | FW_ASSERT(this->m_lastCompletedType != Fw::FilePacket::T_NONE, | |
| 470 | static_cast<FwAssertArgType>(this->m_lastCompletedType)); | ||
| 471 | ✗ | FW_ASSERT(this->m_mode.get() == Mode::CANCEL || this->m_mode.get() == Mode::DOWNLINK, | |
| 472 | static_cast<FwAssertArgType>(this->m_mode.get())); | ||
| 473 | // If canceled mode and currently downlinking data then send a cancel packet | ||
| 474 | ✗ | if (this->m_mode.get() == Mode::CANCEL && this->m_lastCompletedType == Fw::FilePacket::T_START) { | |
| 475 | ✗ | this->sendCancelPacket(); | |
| 476 | ✗ | this->m_lastCompletedType = Fw::FilePacket::T_CANCEL; | |
| 477 | } | ||
| 478 | // If in downlink mode and currently downlinking data then continue with the next packer | ||
| 479 | ✗ | else if (this->m_mode.get() == Mode::DOWNLINK && this->m_lastCompletedType == Fw::FilePacket::T_START) { | |
| 480 | // Send the next packet, or fail doing so | ||
| 481 | ✗ | const Os::File::Status status = this->sendDataPacket(this->m_byteOffset); | |
| 482 | ✗ | if (status != Os::File::OP_OK) { | |
| 483 | ✗ | this->log_WARNING_HI_SendDataFail(this->m_file.getSourceName(), this->m_byteOffset); | |
| 484 | ✗ | this->enterCooldown(); | |
| 485 | ✗ | this->sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK | |
| 486 | : SendFileStatus::STATUS_ERROR); | ||
| 487 | // Don't go to wait state | ||
| 488 | ✗ | return; | |
| 489 | } | ||
| 490 | } | ||
| 491 | // If in downlink mode or cancel and finished downlinking data then send the last packet | ||
| 492 | ✗ | else if (this->m_lastCompletedType == Fw::FilePacket::T_DATA) { | |
| 493 | ✗ | this->sendEndPacket(); | |
| 494 | ✗ | this->m_lastCompletedType = Fw::FilePacket::T_END; | |
| 495 | } | ||
| 496 | ✗ | this->m_mode.set(Mode::WAIT); | |
| 497 | ✗ | this->m_curTimer = 0; | |
| 498 | } | ||
| 499 | |||
| 500 | ✗ | void FileDownlink ::finishHelper(bool cancel) { | |
| 501 | // Complete command and switch to IDLE | ||
| 502 | ✗ | if (not cancel) { | |
| 503 | ✗ | this->m_filesSent.fileSent(); | |
| 504 | ✗ | this->log_ACTIVITY_HI_FileSent(this->m_file.getSourceName(), this->m_file.getDestName()); | |
| 505 | } else { | ||
| 506 | ✗ | this->log_ACTIVITY_HI_DownlinkCanceled(this->m_file.getSourceName(), this->m_file.getDestName()); | |
| 507 | } | ||
| 508 | ✗ | this->enterCooldown(); | |
| 509 | ✗ | sendResponse(SendFileStatus::STATUS_OK); | |
| 510 | ✗ | } | |
| 511 | |||
| 512 | ✗ | void FileDownlink ::getBuffer(Fw::Buffer& buffer, PacketType type) { | |
| 513 | // Check type is correct | ||
| 514 | ✗ | FW_ASSERT(type < COUNT_PACKET_TYPE && type >= 0, static_cast<FwAssertArgType>(type)); | |
| 515 | // Wrap the buffer around our indexed memory. | ||
| 516 | // Set a known ID (context) to look for later | ||
| 517 | ✗ | buffer.set(this->m_memoryStore[type], FILEDOWNLINK_INTERNAL_BUFFER_SIZE, m_lastBufferId); | |
| 518 | ✗ | m_lastBufferId++; | |
| 519 | ✗ | } | |
| 520 | } // end namespace Svc | ||
| 521 |