| 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 | 9 | FileDownlink ::FileDownlink(const char* const name) | |
| 27 | : FileDownlinkComponentBase(name), | ||
| 28 | 9 | m_configured(false), | |
| 29 | 9 | m_filesSent(this), | |
| 30 | 9 | m_packetsSent(this), | |
| 31 | 9 | m_warnings(this), | |
| 32 | 9 | m_sequenceIndex(0), | |
| 33 | 9 | m_curTimer(0), | |
| 34 | 9 | m_bufferSize(0), | |
| 35 | 9 | m_byteOffset(0), | |
| 36 | 9 | m_endOffset(0), | |
| 37 | 9 | m_lastCompletedType(Fw::FilePacket::T_NONE), | |
| 38 | 9 | m_lastBufferId(0), | |
| 39 |
1/1✓ Branch 7 taken 9 times.
|
9 | m_curEntry(), |
| 40 |
4/4✓ Branch 10 taken 9 times.
✓ Branch 16 taken 9 times.
✓ Branch 22 taken 9 times.
✓ Branch 28 taken 9 times.
|
36 | m_cntxId(0) {} |
| 41 | |||
| 42 | 9 | void FileDownlink ::configure(U32 cooldown, U32 cycleTime, U32 fileQueueDepth) { | |
| 43 | 9 | this->m_cooldown = cooldown; | |
| 44 | 9 | this->m_cycleTime = cycleTime; | |
| 45 | 9 | this->m_configured = true; | |
| 46 | |||
| 47 | Os::Queue::Status stat = | ||
| 48 |
3/3✓ Branch 7 taken 9 times.
✓ Branch 15 taken 9 times.
✓ Branch 18 taken 9 times.
|
9 | m_fileQueue.create(this->getInstance(), Os::QueueString("fileDownlinkQueue"), |
| 49 | static_cast<FwSizeType>(fileQueueDepth), static_cast<FwSizeType>(sizeof(struct FileEntry))); | ||
| 50 | 9 | FW_ASSERT(stat == Os::Queue::OP_OK, static_cast<FwAssertArgType>(stat)); | |
| 51 | 9 | } | |
| 52 | |||
| 53 | 9 | void FileDownlink ::configure(const char* directory) { | |
| 54 | 9 | this->m_file.configureSandbox(directory); | |
| 55 | 9 | } | |
| 56 | |||
| 57 | 9 | void FileDownlink ::deinit() { | |
| 58 | 9 | this->m_fileQueue.teardown(); | |
| 59 | 9 | FileDownlinkComponentBase::deinit(); | |
| 60 | 9 | } | |
| 61 | |||
| 62 | ✗ | void FileDownlink ::preamble() { | |
| 63 | ✗ | FW_ASSERT(this->m_configured == true); | |
| 64 | ✗ | } | |
| 65 | |||
| 66 | 18 | FileDownlink ::~FileDownlink() {} | |
| 67 | |||
| 68 | // ---------------------------------------------------------------------- | ||
| 69 | // Handler implementations for user-defined typed input ports | ||
| 70 | // ---------------------------------------------------------------------- | ||
| 71 | |||
| 72 | 47 | void FileDownlink ::Run_handler(const FwIndexType portNum, U32 context) { | |
| 73 |
3/4✓ Branch 4 taken 9 times.
✓ Branch 5 taken 30 times.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
|
47 | switch (this->m_mode.get()) { |
| 74 | 9 | case Mode::IDLE: { | |
| 75 | 9 | FwSizeType real_size = 0; | |
| 76 | 9 | FwQueuePriorityType prio = 0; | |
| 77 |
1/1✓ Branch 8 taken 9 times.
|
9 | 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 |
3/4✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
|
9 | if (stat != Os::Queue::Status::OP_OK || sizeof(this->m_curEntry) != real_size) { |
| 82 | 1 | return; | |
| 83 | } | ||
| 84 | |||
| 85 |
1/1✓ Branch 16 taken 8 times.
|
16 | sendFile(this->m_curEntry.srcFilename, this->m_curEntry.destFilename, this->m_curEntry.offset, |
| 86 | 8 | this->m_curEntry.length); | |
| 87 | 8 | break; | |
| 88 | } | ||
| 89 | 30 | case Mode::COOLDOWN: { | |
| 90 |
2/2✓ Branch 8 taken 5 times.
✓ Branch 9 taken 25 times.
|
30 | if (this->m_curTimer >= this->m_cooldown) { |
| 91 | 5 | this->m_curTimer = 0; | |
| 92 | 5 | this->m_mode.set(Mode::IDLE); | |
| 93 | } else { | ||
| 94 | 25 | this->m_curTimer += m_cycleTime; | |
| 95 | } | ||
| 96 | 30 | break; | |
| 97 | } | ||
| 98 | 8 | case Mode::WAIT: { | |
| 99 | 8 | this->m_curTimer += m_cycleTime; | |
| 100 | 8 | break; | |
| 101 | } | ||
| 102 | ✗ | default: | |
| 103 | ✗ | break; | |
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | 1 | 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 |
1/1✓ Branch 2 taken 1 times.
|
1 | struct FileEntry entry; |
| 114 | 1 | entry.offset = offset; | |
| 115 | 1 | entry.length = length; | |
| 116 | 1 | entry.source = FileDownlink::PORT; | |
| 117 | 1 | entry.opCode = 0; | |
| 118 | 1 | entry.cmdSeq = 0; | |
| 119 | 1 | entry.context = m_cntxId++; | |
| 120 | |||
| 121 | // Guard against filename overflow | ||
| 122 |
2/3✓ Branch 11 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 1 times.
|
1 | if (sourceFilename.length() >= entry.srcFilename.getCapacity()) { |
| 123 | ✗ | this->log_WARNING_HI_FilenameSourceOverflow(); | |
| 124 | ✗ | return SendFileResponse(SendFileStatus::STATUS_ERROR, std::numeric_limits<U32>::max()); | |
| 125 |
2/3✓ Branch 11 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 1 times.
|
1 | } 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 |
1/1✓ Branch 5 taken 1 times.
|
1 | entry.srcFilename = sourceFilename; |
| 130 |
1/1✓ Branch 5 taken 1 times.
|
1 | entry.destFilename = destFilename; |
| 131 | |||
| 132 | Os::Queue::Status status = | ||
| 133 |
1/1✓ Branch 6 taken 1 times.
|
1 | m_fileQueue.send(reinterpret_cast<U8*>(&entry), static_cast<FwSizeType>(sizeof(entry)), 0, |
| 134 | Os::Queue::BlockingType::NONBLOCKING); | ||
| 135 | |||
| 136 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (status != Os::Queue::Status::OP_OK) { |
| 137 | ✗ | return SendFileResponse(SendFileStatus::STATUS_ERROR, std::numeric_limits<U32>::max()); | |
| 138 | } | ||
| 139 |
1/1✓ Branch 1 taken 1 times.
|
1 | return SendFileResponse(SendFileStatus::STATUS_OK, entry.context); |
| 140 | } | ||
| 141 | 1 | } | |
| 142 | |||
| 143 | ✗ | void FileDownlink ::pingIn_handler(const FwIndexType portNum, U32 key) { | |
| 144 | ✗ | this->pingOut_out(0, key); | |
| 145 | ✗ | } | |
| 146 | |||
| 147 | 14 | 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 |
3/6✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 14 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 14 times.
|
14 | 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 | 14 | 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 |
6/8✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 4 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 10 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 9 times.
|
14 | if (this->m_lastCompletedType == Fw::FilePacket::T_END || this->m_lastCompletedType == Fw::FilePacket::T_CANCEL) { |
| 158 |
1/2✗ Branch 6 not taken.
✓ Branch 7 taken 5 times.
|
5 | finishHelper(this->m_lastCompletedType == Fw::FilePacket::T_CANCEL); |
| 159 | 5 | return; | |
| 160 | } | ||
| 161 | // If waiting and a buffer is in-bound, then switch to downlink mode | ||
| 162 |
2/2✓ Branch 4 taken 8 times.
✓ Branch 5 taken 1 times.
|
9 | else if (this->m_mode.get() == Mode::WAIT) { |
| 163 | 8 | this->m_mode.set(Mode::DOWNLINK); | |
| 164 | } | ||
| 165 | |||
| 166 | 9 | this->downlinkPacket(); | |
| 167 | } | ||
| 168 | |||
| 169 | // ---------------------------------------------------------------------- | ||
| 170 | // Command handler implementations | ||
| 171 | // ---------------------------------------------------------------------- | ||
| 172 | |||
| 173 | 4 | void FileDownlink ::SendFile_cmdHandler(const FwOpcodeType opCode, | |
| 174 | const U32 cmdSeq, | ||
| 175 | const Fw::CmdStringArg& sourceFilename, | ||
| 176 | const Fw::CmdStringArg& destFilename) { | ||
| 177 |
1/1✓ Branch 2 taken 4 times.
|
4 | struct FileEntry entry; |
| 178 | 4 | entry.offset = 0; | |
| 179 | 4 | entry.length = 0; | |
| 180 | 4 | entry.source = FileDownlink::COMMAND; | |
| 181 | 4 | entry.opCode = opCode; | |
| 182 | 4 | entry.cmdSeq = cmdSeq; | |
| 183 | 4 | entry.context = std::numeric_limits<U32>::max(); | |
| 184 | |||
| 185 | // Guard against filename overflow | ||
| 186 |
2/3✓ Branch 7 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
|
4 | if (sourceFilename.length() >= entry.srcFilename.getCapacity()) { |
| 187 | ✗ | this->log_WARNING_HI_FilenameSourceOverflow(); | |
| 188 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); | |
| 189 |
2/3✓ Branch 7 taken 4 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
|
4 | } 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 |
1/1✓ Branch 5 taken 4 times.
|
4 | entry.srcFilename = sourceFilename; |
| 194 |
1/1✓ Branch 5 taken 4 times.
|
4 | entry.destFilename = destFilename; |
| 195 | |||
| 196 | Os::Queue::Status status = | ||
| 197 |
1/1✓ Branch 6 taken 4 times.
|
4 | m_fileQueue.send(reinterpret_cast<U8*>(&entry), static_cast<FwSizeType>(sizeof(entry)), 0, |
| 198 | Os::Queue::BlockingType::NONBLOCKING); | ||
| 199 | |||
| 200 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (status != Os::Queue::Status::OP_OK) { |
| 201 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); | |
| 202 | } | ||
| 203 | } | ||
| 204 | 8 | } | |
| 205 | |||
| 206 | 3 | 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 |
1/1✓ Branch 2 taken 3 times.
|
3 | struct FileEntry entry; |
| 213 | 3 | entry.offset = startOffset; | |
| 214 | 3 | entry.length = length; | |
| 215 | 3 | entry.source = FileDownlink::COMMAND; | |
| 216 | 3 | entry.opCode = opCode; | |
| 217 | 3 | entry.cmdSeq = cmdSeq; | |
| 218 | 3 | entry.context = std::numeric_limits<U32>::max(); | |
| 219 | |||
| 220 | // Guard against filename overflow | ||
| 221 |
2/3✓ Branch 7 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
|
3 | if (sourceFilename.length() >= entry.srcFilename.getCapacity()) { |
| 222 | ✗ | this->log_WARNING_HI_FilenameSourceOverflow(); | |
| 223 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); | |
| 224 |
2/3✓ Branch 7 taken 3 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 3 times.
|
3 | } 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 |
1/1✓ Branch 5 taken 3 times.
|
3 | entry.srcFilename = sourceFilename; |
| 229 |
1/1✓ Branch 5 taken 3 times.
|
3 | entry.destFilename = destFilename; |
| 230 | |||
| 231 | Os::Queue::Status status = | ||
| 232 |
1/1✓ Branch 6 taken 3 times.
|
3 | m_fileQueue.send(reinterpret_cast<U8*>(&entry), static_cast<FwSizeType>(sizeof(entry)), 0, |
| 233 | Os::Queue::BlockingType::NONBLOCKING); | ||
| 234 | |||
| 235 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (status != Os::Queue::Status::OP_OK) { |
| 236 | ✗ | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); | |
| 237 | } | ||
| 238 | } | ||
| 239 | 6 | } | |
| 240 | |||
| 241 | 2 | void FileDownlink ::Cancel_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq) { | |
| 242 | // Must be able to cancel in both downlink and waiting states | ||
| 243 |
5/6✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
|
2 | if (this->m_mode.get() == Mode::DOWNLINK || this->m_mode.get() == Mode::WAIT) { |
| 244 | 1 | this->m_mode.set(Mode::CANCEL); | |
| 245 | } | ||
| 246 |
2/2✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
|
2 | this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); |
| 247 | 2 | } | |
| 248 | |||
| 249 | // ---------------------------------------------------------------------- | ||
| 250 | // Private helper methods | ||
| 251 | // ---------------------------------------------------------------------- | ||
| 252 | |||
| 253 | 7 | Fw::CmdResponse FileDownlink ::statusToCmdResp(SendFileStatus status) { | |
| 254 |
2/7✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
7 | switch (status.e) { |
| 255 | 7 | case SendFileStatus::STATUS_OK: | |
| 256 | 7 | 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 | 8 | void FileDownlink ::sendResponse(SendFileStatus resp) { | |
| 273 | 8 | FW_ASSERT(this->m_curEntry.source == FileDownlink::COMMAND || this->m_curEntry.source == FileDownlink::PORT, | |
| 274 | static_cast<FwAssertArgType>(this->m_curEntry.source)); | ||
| 275 |
3/4✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 1 times.
|
8 | if (this->m_curEntry.source == FileDownlink::COMMAND) { |
| 276 |
3/3✓ Branch 7 taken 7 times.
✓ Branch 10 taken 7 times.
✓ Branch 21 taken 7 times.
|
7 | this->cmdResponse_out(this->m_curEntry.opCode, this->m_curEntry.cmdSeq, statusToCmdResp(resp)); |
| 277 | } else { | ||
| 278 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
|
2 | for (FwIndexType i = 0; i < this->getNum_FileComplete_OutputPorts(); i++) { |
| 279 |
1/2✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | if (this->isConnected_FileComplete_OutputPort(i)) { |
| 280 |
2/2✓ Branch 13 taken 1 times.
✓ Branch 17 taken 1 times.
|
1 | this->FileComplete_out(i, Svc::SendFileResponse(resp, this->m_curEntry.context)); |
| 281 | } | ||
| 282 | } | ||
| 283 | } | ||
| 284 | 8 | } | |
| 285 | |||
| 286 | 8 | void FileDownlink ::sendFile(const Fw::FileNameString& sourceFilename, | |
| 287 | const Fw::FileNameString& destFilename, | ||
| 288 | U32 startOffset, | ||
| 289 | U32 length) { | ||
| 290 | // Open file for downlink | ||
| 291 | 8 | Os::File::Status status = this->m_file.open(sourceFilename, destFilename); | |
| 292 | |||
| 293 | // Reject command if error when opening file | ||
| 294 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
|
8 | if (status != Os::File::OP_OK) { |
| 295 | 2 | this->m_mode.set(Mode::IDLE); | |
| 296 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (status == Os::File::OUTSIDE_SANDBOX) { |
| 297 | 1 | this->m_warnings.sourceOutOfSandbox(); | |
| 298 | } else { | ||
| 299 | 1 | this->m_warnings.fileOpenError(); | |
| 300 | } | ||
| 301 |
2/2✓ Branch 5 taken 2 times.
✓ Branch 8 taken 2 times.
|
2 | sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK : SendFileStatus::STATUS_ERROR); |
| 302 | 2 | return; | |
| 303 | } | ||
| 304 | 6 | const U32 fileSize = this->m_file.getSize(); | |
| 305 | |||
| 306 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | 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 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
|
6 | } else if (startOffset >= fileSize) { |
| 315 | 1 | this->enterCooldown(); | |
| 316 | 1 | this->log_WARNING_HI_DownlinkPartialFail(this->m_file.getSourceName(), this->m_file.getDestName(), startOffset, | |
| 317 | fileSize); | ||
| 318 |
2/2✓ Branch 5 taken 1 times.
✓ Branch 8 taken 1 times.
|
1 | sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK |
| 319 | : SendFileStatus::STATUS_INVALID); | ||
| 320 | 1 | return; | |
| 321 | // startOffset < fileSize here, so the subtraction cannot underflow and, unlike | ||
| 322 | // startOffset + length, cannot wrap | ||
| 323 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
|
5 | } 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 | 2 | this->log_WARNING_LO_DownlinkPartialWarning(startOffset, length, fileSize, this->m_file.getSourceName(), | |
| 327 | 1 | this->m_file.getDestName()); | |
| 328 | 1 | length = fileSize - startOffset; | |
| 329 | } | ||
| 330 | |||
| 331 | // Send file and switch to WAIT mode | ||
| 332 | 5 | this->getBuffer(this->m_buffer, FILE_PACKET); | |
| 333 | 5 | this->sendStartPacket(); | |
| 334 | 5 | this->m_mode.set(Mode::WAIT); | |
| 335 | 5 | this->m_sequenceIndex = 1; | |
| 336 | 5 | this->m_curTimer = 0; | |
| 337 | 5 | this->m_byteOffset = startOffset; | |
| 338 | 5 | this->m_lastCompletedType = Fw::FilePacket::T_START; | |
| 339 | |||
| 340 | // zero length means read until end of file | ||
| 341 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
|
5 | if (length > 0) { |
| 342 | 2 | this->log_ACTIVITY_HI_SendStarted(length, this->m_file.getSourceName(), this->m_file.getDestName()); | |
| 343 | 2 | this->m_endOffset = startOffset + length; | |
| 344 | } else { | ||
| 345 | 6 | this->log_ACTIVITY_HI_SendStarted(fileSize - startOffset, this->m_file.getSourceName(), | |
| 346 | 3 | this->m_file.getDestName()); | |
| 347 | 3 | this->m_endOffset = fileSize; | |
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | 6 | Os::File::Status FileDownlink ::sendDataPacket(U32& byteOffset) { | |
| 352 | 6 | 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 |
2/2✓ Branch 5 taken 2 times.
✓ Branch 6 taken 4 times.
|
6 | if (byteOffset >= this->m_endOffset) { |
| 356 | 2 | 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 | 4 | const U32 remaining = this->m_endOffset - byteOffset; | |
| 363 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | const U32 dataSize = (remaining < maxDataSize) ? remaining : maxDataSize; |
| 364 | 4 | U8 buffer[maxDataSize]; | |
| 365 | // This will be last data packet sent | ||
| 366 |
1/2✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
|
4 | if (dataSize + byteOffset == this->m_endOffset) { |
| 367 | 4 | this->m_lastCompletedType = Fw::FilePacket::T_DATA; | |
| 368 | } | ||
| 369 | |||
| 370 |
1/1✓ Branch 5 taken 4 times.
|
4 | const Os::File::Status status = this->m_file.read(buffer, byteOffset, dataSize); |
| 371 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (status != Os::File::OP_OK) { |
| 372 | ✗ | this->m_warnings.fileRead(status); | |
| 373 | ✗ | return status; | |
| 374 | } | ||
| 375 | |||
| 376 | 4 | Fw::FilePacket::DataPacket dataPacket; | |
| 377 |
1/1✓ Branch 6 taken 4 times.
|
4 | dataPacket.initialize(this->m_sequenceIndex, byteOffset, static_cast<U16>(dataSize), buffer); |
| 378 | 4 | ++this->m_sequenceIndex; | |
| 379 | 4 | Fw::FilePacket filePacket; | |
| 380 |
1/1✓ Branch 1 taken 4 times.
|
4 | filePacket.fromDataPacket(dataPacket); |
| 381 |
1/1✓ Branch 4 taken 4 times.
|
4 | this->sendFilePacket(filePacket); |
| 382 | |||
| 383 | 4 | byteOffset += dataSize; | |
| 384 | |||
| 385 | 4 | return Os::File::OP_OK; | |
| 386 | } | ||
| 387 | |||
| 388 | 1 | void FileDownlink ::sendCancelPacket() { | |
| 389 |
1/1✓ Branch 2 taken 1 times.
|
1 | Fw::Buffer buffer; |
| 390 | 1 | Fw::FilePacket::CancelPacket cancelPacket; | |
| 391 |
1/1✓ Branch 5 taken 1 times.
|
1 | cancelPacket.initialize(this->m_sequenceIndex); |
| 392 | |||
| 393 | 1 | Fw::FilePacket filePacket; | |
| 394 |
1/1✓ Branch 1 taken 1 times.
|
1 | filePacket.fromCancelPacket(cancelPacket); |
| 395 |
1/1✓ Branch 4 taken 1 times.
|
1 | this->getBuffer(buffer, CANCEL_PACKET); |
| 396 | 1 | 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 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 8 taken 1 times.
|
1 | buffer.getSerializer().serializeFrom(static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_FILE)); |
| 403 | 1 | FW_ASSERT(status == Fw::FW_SERIALIZE_OK); | |
| 404 |
1/1✓ Branch 2 taken 1 times.
|
2 | Fw::Buffer offsetBuffer(buffer.getData() + sizeof(FwPacketDescriptorType), |
| 405 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 7 taken 1 times.
|
2 | buffer.getSize() - static_cast<Fw::Buffer::SizeType>(sizeof(FwPacketDescriptorType))); |
| 406 | // Serialize the filePacket content into the buffer | ||
| 407 |
1/1✓ Branch 1 taken 1 times.
|
1 | status = filePacket.toBuffer(offsetBuffer); |
| 408 | 1 | FW_ASSERT(status == Fw::FW_SERIALIZE_OK); | |
| 409 |
1/1✓ Branch 1 taken 1 times.
|
1 | const U32 bufferSize = filePacket.bufferSize() + static_cast<U32>(sizeof(FwPacketDescriptorType)); |
| 410 | 1 | FW_ASSERT(buffer.getSize() >= bufferSize, static_cast<FwAssertArgType>(buffer.getSize()), | |
| 411 | static_cast<FwAssertArgType>(bufferSize)); | ||
| 412 |
1/1✓ Branch 2 taken 1 times.
|
1 | buffer.setSize(bufferSize); |
| 413 |
1/1✓ Branch 5 taken 1 times.
|
1 | this->bufferSendOut_out(0, buffer); |
| 414 |
1/1✓ Branch 4 taken 1 times.
|
1 | this->m_packetsSent.packetSent(); |
| 415 | 2 | } | |
| 416 | |||
| 417 | 4 | void FileDownlink ::sendEndPacket() { | |
| 418 |
1/1✓ Branch 2 taken 4 times.
|
4 | CFDP::Checksum checksum; |
| 419 |
1/1✓ Branch 4 taken 4 times.
|
4 | this->m_file.getChecksum(checksum); |
| 420 | |||
| 421 | 4 | Fw::FilePacket::EndPacket endPacket; | |
| 422 |
1/1✓ Branch 5 taken 4 times.
|
4 | endPacket.initialize(this->m_sequenceIndex, checksum); |
| 423 | |||
| 424 | 4 | Fw::FilePacket filePacket; | |
| 425 |
1/1✓ Branch 1 taken 4 times.
|
4 | filePacket.fromEndPacket(endPacket); |
| 426 |
1/1✓ Branch 4 taken 4 times.
|
4 | this->sendFilePacket(filePacket); |
| 427 | 8 | } | |
| 428 | |||
| 429 | 5 | void FileDownlink ::sendStartPacket() { | |
| 430 | 5 | Fw::FilePacket::StartPacket startPacket; | |
| 431 |
1/1✓ Branch 14 taken 5 times.
|
5 | startPacket.initialize(this->m_file.getSize(), this->m_file.getSourceName().toChar(), |
| 432 | 5 | this->m_file.getDestName().toChar()); | |
| 433 | 5 | Fw::FilePacket filePacket; | |
| 434 |
1/1✓ Branch 1 taken 5 times.
|
5 | filePacket.fromStartPacket(startPacket); |
| 435 |
1/1✓ Branch 4 taken 5 times.
|
5 | this->sendFilePacket(filePacket); |
| 436 | 5 | } | |
| 437 | |||
| 438 | 13 | void FileDownlink ::sendFilePacket(const Fw::FilePacket& filePacket) { | |
| 439 |
1/1✓ Branch 2 taken 13 times.
|
13 | const U32 bufferSize = filePacket.bufferSize() + static_cast<U32>(sizeof(FwPacketDescriptorType)); |
| 440 | 13 | FW_ASSERT(this->m_buffer.getData() != nullptr); | |
| 441 | 13 | 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 |
2/2✓ Branch 4 taken 13 times.
✓ Branch 10 taken 13 times.
|
13 | Fw::SerializeStatus status = this->m_buffer.getSerializer().serializeFrom( |
| 445 | static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_FILE)); | ||
| 446 | 13 | FW_ASSERT(status == Fw::FW_SERIALIZE_OK); | |
| 447 | // Serialize the filePacket content into the buffer, offset by the size of the packet descriptor | ||
| 448 | 13 | Fw::Buffer offsetBuffer( | |
| 449 |
1/1✓ Branch 5 taken 13 times.
|
13 | this->m_buffer.getData() + sizeof(FwPacketDescriptorType), |
| 450 |
2/2✓ Branch 6 taken 13 times.
✓ Branch 11 taken 13 times.
|
26 | this->m_buffer.getSize() - static_cast<Fw::Buffer::SizeType>(sizeof(FwPacketDescriptorType))); |
| 451 |
1/1✓ Branch 2 taken 13 times.
|
13 | status = filePacket.toBuffer(offsetBuffer); |
| 452 | 13 | FW_ASSERT(status == Fw::FW_SERIALIZE_OK); | |
| 453 | // set the buffer size to the packet size | ||
| 454 |
1/1✓ Branch 6 taken 13 times.
|
13 | this->m_buffer.setSize(bufferSize); |
| 455 |
1/1✓ Branch 8 taken 13 times.
|
13 | this->bufferSendOut_out(0, this->m_buffer); |
| 456 | // restore buffer size to max | ||
| 457 |
1/1✓ Branch 6 taken 13 times.
|
13 | this->m_buffer.setSize(FILEDOWNLINK_INTERNAL_BUFFER_SIZE); |
| 458 |
1/1✓ Branch 4 taken 13 times.
|
13 | this->m_packetsSent.packetSent(); |
| 459 | 26 | } | |
| 460 | |||
| 461 | 6 | void FileDownlink ::enterCooldown() { | |
| 462 | 6 | this->m_file.getOsFile().close(); | |
| 463 | 6 | this->m_mode.set(Mode::COOLDOWN); | |
| 464 | 6 | this->m_lastCompletedType = Fw::FilePacket::T_NONE; | |
| 465 | 6 | this->m_curTimer = 0; | |
| 466 | 6 | } | |
| 467 | |||
| 468 | 9 | void FileDownlink ::downlinkPacket() { | |
| 469 | 9 | FW_ASSERT(this->m_lastCompletedType != Fw::FilePacket::T_NONE, | |
| 470 | static_cast<FwAssertArgType>(this->m_lastCompletedType)); | ||
| 471 | 9 | 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 |
6/8✓ Branch 4 taken 1 times.
✓ Branch 5 taken 8 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 8 times.
|
9 | if (this->m_mode.get() == Mode::CANCEL && this->m_lastCompletedType == Fw::FilePacket::T_START) { |
| 475 | 1 | this->sendCancelPacket(); | |
| 476 | 1 | this->m_lastCompletedType = Fw::FilePacket::T_CANCEL; | |
| 477 | } | ||
| 478 | // If in downlink mode and currently downlinking data then continue with the next packer | ||
| 479 |
6/8✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 8 times.
✓ Branch 11 taken 4 times.
✓ Branch 12 taken 4 times.
✓ Branch 13 taken 4 times.
✓ Branch 14 taken 4 times.
|
8 | 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 | 4 | const Os::File::Status status = this->sendDataPacket(this->m_byteOffset); | |
| 482 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | 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 |
2/4✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
|
4 | else if (this->m_lastCompletedType == Fw::FilePacket::T_DATA) { |
| 493 | 4 | this->sendEndPacket(); | |
| 494 | 4 | this->m_lastCompletedType = Fw::FilePacket::T_END; | |
| 495 | } | ||
| 496 | 9 | this->m_mode.set(Mode::WAIT); | |
| 497 | 9 | this->m_curTimer = 0; | |
| 498 | } | ||
| 499 | |||
| 500 | 5 | void FileDownlink ::finishHelper(bool cancel) { | |
| 501 | // Complete command and switch to IDLE | ||
| 502 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
5 | if (not cancel) { |
| 503 | 4 | this->m_filesSent.fileSent(); | |
| 504 | 4 | this->log_ACTIVITY_HI_FileSent(this->m_file.getSourceName(), this->m_file.getDestName()); | |
| 505 | } else { | ||
| 506 | 1 | this->log_ACTIVITY_HI_DownlinkCanceled(this->m_file.getSourceName(), this->m_file.getDestName()); | |
| 507 | } | ||
| 508 | 5 | this->enterCooldown(); | |
| 509 |
2/2✓ Branch 5 taken 5 times.
✓ Branch 8 taken 5 times.
|
5 | sendResponse(SendFileStatus::STATUS_OK); |
| 510 | 5 | } | |
| 511 | |||
| 512 | 6 | void FileDownlink ::getBuffer(Fw::Buffer& buffer, PacketType type) { | |
| 513 | // Check type is correct | ||
| 514 | 6 | 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 | 6 | buffer.set(this->m_memoryStore[type], FILEDOWNLINK_INTERNAL_BUFFER_SIZE, m_lastBufferId); | |
| 518 | 6 | m_lastBufferId++; | |
| 519 | 6 | } | |
| 520 | } // end namespace Svc | ||
| 521 |