F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
Loading...
Searching...
No Matches
FileDownlink.cpp
Go to the documentation of this file.
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
13#include <Fw/Types/Assert.hpp>
14#include <FpConfig.hpp>
16#include <Os/QueueString.hpp>
17#include <limits>
18
19namespace Svc {
20
21 // ----------------------------------------------------------------------
22 // Construction, initialization, and destruction
23 // ----------------------------------------------------------------------
24
25 FileDownlink ::
26 FileDownlink(
27 const char *const name
28 ) :
30 m_configured(false),
31 m_filesSent(this),
32 m_packetsSent(this),
33 m_warnings(this),
34 m_sequenceIndex(0),
35 m_curTimer(0),
36 m_bufferSize(0),
37 m_byteOffset(0),
38 m_endOffset(0),
39 m_lastCompletedType(Fw::FilePacket::T_NONE),
40 m_lastBufferId(0),
41 m_curEntry(),
42 m_cntxId(0)
43 {
44 }
45
46 void FileDownlink ::
47 configure(
48 U32 timeout,
49 U32 cooldown,
50 U32 cycleTime,
51 U32 fileQueueDepth
52 )
53 {
54 this->m_timeout = timeout;
55 this->m_cooldown = cooldown;
56 this->m_cycleTime = cycleTime;
57 this->m_configured = true;
58
59 Os::Queue::Status stat = m_fileQueue.create(
60 Os::QueueString("fileDownlinkQueue"),
61 static_cast<FwSizeType>(fileQueueDepth),
62 static_cast<FwSizeType>(sizeof(struct FileEntry))
63 );
64 FW_ASSERT(stat == Os::Queue::OP_OK, stat);
65 }
66
67 void FileDownlink ::
68 preamble()
69 {
70 FW_ASSERT(this->m_configured == true);
71 }
72
73 FileDownlink ::
74 ~FileDownlink()
75 {
76
77 }
78
79 // ----------------------------------------------------------------------
80 // Handler implementations for user-defined typed input ports
81 // ----------------------------------------------------------------------
82
83 void FileDownlink ::
84 Run_handler(
85 const NATIVE_INT_TYPE portNum,
86 U32 context
87 )
88 {
89 switch(this->m_mode.get())
90 {
91 case Mode::IDLE: {
92 FwSizeType real_size = 0;
93 FwQueuePriorityType prio = 0;
94 Os::Queue::Status stat = m_fileQueue.receive(
95 reinterpret_cast<U8*>(&this->m_curEntry),
96 static_cast<FwSizeType>(sizeof(this->m_curEntry)),
98 real_size,
99 prio
100 );
101
102 if(stat != Os::Queue::Status::OP_OK || sizeof(this->m_curEntry) != real_size) {
103 return;
104 }
105
106 sendFile(
107 this->m_curEntry.srcFilename,
108 this->m_curEntry.destFilename,
109 this->m_curEntry.offset,
110 this->m_curEntry.length
111 );
112 break;
113 }
114 case Mode::COOLDOWN: {
115 if (this->m_curTimer >= this->m_cooldown) {
116 this->m_curTimer = 0;
117 this->m_mode.set(Mode::IDLE);
118 } else {
119 this->m_curTimer += m_cycleTime;
120 }
121 break;
122 }
123 case Mode::WAIT: {
124 //If current timeout is too-high and we are waiting for a packet, issue a timeout
125 if (this->m_curTimer >= this->m_timeout) {
126 this->m_curTimer = 0;
127 this->log_WARNING_HI_DownlinkTimeout(this->m_file.getSourceName(), this->m_file.getDestName());
128 this->enterCooldown();
129 this->sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK : SendFileStatus::STATUS_ERROR);
130 } else { //Otherwise update the current counter
131 this->m_curTimer += m_cycleTime;
132 }
133 break;
134 }
135 default:
136 break;
137 }
138 }
139
140 Svc::SendFileResponse FileDownlink ::
141 SendFile_handler(
142 const NATIVE_INT_TYPE portNum,
143 const Fw::StringBase& sourceFilename, // lgtm[cpp/large-parameter] dictated by command architecture
144 const Fw::StringBase& destFilename, // lgtm[cpp/large-parameter] dictated by command architecture
145 U32 offset,
146 U32 length
147 )
148 {
149 struct FileEntry entry;
150 entry.srcFilename[0] = 0;
151 entry.destFilename[0] = 0;
152 entry.offset = offset;
153 entry.length = length;
154 entry.source = FileDownlink::PORT;
155 entry.opCode = 0;
156 entry.cmdSeq = 0;
157 entry.context = m_cntxId++;
158
159 FW_ASSERT(sourceFilename.length() < sizeof(entry.srcFilename));
160 FW_ASSERT(destFilename.length() < sizeof(entry.destFilename));
161 (void) Fw::StringUtils::string_copy(entry.srcFilename, sourceFilename.toChar(), static_cast<FwSizeType>(sizeof(entry.srcFilename)));
162 (void) Fw::StringUtils::string_copy(entry.destFilename, destFilename.toChar(), static_cast<FwSizeType>(sizeof(entry.destFilename)));
163
164 Os::Queue::Status status = m_fileQueue.send(reinterpret_cast<U8*>(&entry), static_cast<FwSizeType>(sizeof(entry)), 0, Os::Queue::BlockingType::NONBLOCKING);
165
166 if(status != Os::Queue::Status::OP_OK) {
167 return SendFileResponse(SendFileStatus::STATUS_ERROR, std::numeric_limits<U32>::max());
168 }
169 return SendFileResponse(SendFileStatus::STATUS_OK, entry.context);
170 }
171
172 void FileDownlink ::
173 pingIn_handler(
174 const NATIVE_INT_TYPE portNum,
175 U32 key
176 )
177 {
178 this->pingOut_out(0,key);
179 }
180
181 void FileDownlink ::
182 bufferReturn_handler(
183 const NATIVE_INT_TYPE portNum,
184 Fw::Buffer &fwBuffer
185 )
186 {
187 //If this is a stale buffer (old, timed-out, or both), then ignore its return.
188 //File downlink actions only respond to the return of the most-recently-sent buffer.
189 if (this->m_lastBufferId != fwBuffer.getContext() + 1 ||
190 this->m_mode.get() == Mode::IDLE) {
191 return;
192 }
193 //Non-ignored buffers cannot be returned in "DOWNLINK" and "IDLE" state. Only in "WAIT", "CANCEL" state.
194 FW_ASSERT(this->m_mode.get() == Mode::WAIT || this->m_mode.get() == Mode::CANCEL, this->m_mode.get());
195 //If the last packet has been sent (and is returning now) then finish the file
196 if (this->m_lastCompletedType == Fw::FilePacket::T_END ||
197 this->m_lastCompletedType == Fw::FilePacket::T_CANCEL) {
198 finishHelper(this->m_lastCompletedType == Fw::FilePacket::T_CANCEL);
199 return;
200 }
201 //If waiting and a buffer is in-bound, then switch to downlink mode
202 else if (this->m_mode.get() == Mode::WAIT) {
203 this->m_mode.set(Mode::DOWNLINK);
204 }
205
206 this->downlinkPacket();
207 }
208
209 // ----------------------------------------------------------------------
210 // Command handler implementations
211 // ----------------------------------------------------------------------
212
213 void FileDownlink ::
214 SendFile_cmdHandler(
215 const FwOpcodeType opCode,
216 const U32 cmdSeq,
217 const Fw::CmdStringArg& sourceFilename,
218 const Fw::CmdStringArg& destFilename
219 )
220 {
221 struct FileEntry entry;
222 entry.srcFilename[0] = 0;
223 entry.destFilename[0] = 0;
224 entry.offset = 0;
225 entry.length = 0;
226 entry.source = FileDownlink::COMMAND;
227 entry.opCode = opCode;
228 entry.cmdSeq = cmdSeq;
229 entry.context = std::numeric_limits<U32>::max();
230
231
232 FW_ASSERT(sourceFilename.length() < sizeof(entry.srcFilename));
233 FW_ASSERT(destFilename.length() < sizeof(entry.destFilename));
234 (void) Fw::StringUtils::string_copy(entry.srcFilename, sourceFilename.toChar(), static_cast<FwSizeType>(sizeof(entry.srcFilename)));
235 (void) Fw::StringUtils::string_copy(entry.destFilename, destFilename.toChar(), static_cast<FwSizeType>(sizeof(entry.destFilename)));
236
237 Os::Queue::Status status = m_fileQueue.send(reinterpret_cast<U8*>(&entry), static_cast<FwSizeType>(sizeof(entry)), 0, Os::Queue::BlockingType::NONBLOCKING);
238
239 if(status != Os::Queue::Status::OP_OK) {
240 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
241 }
242 }
243
244 void FileDownlink ::
245 SendPartial_cmdHandler(
246 FwOpcodeType opCode,
247 U32 cmdSeq,
248 const Fw::CmdStringArg& sourceFilename,
249 const Fw::CmdStringArg& destFilename,
250 U32 startOffset,
251 U32 length
252 )
253 {
254 struct FileEntry entry;
255 entry.srcFilename[0] = 0;
256 entry.destFilename[0] = 0;
257 entry.offset = startOffset;
258 entry.length = length;
259 entry.source = FileDownlink::COMMAND;
260 entry.opCode = opCode;
261 entry.cmdSeq = cmdSeq;
262 entry.context = std::numeric_limits<U32>::max();
263
264
265 FW_ASSERT(sourceFilename.length() < sizeof(entry.srcFilename));
266 FW_ASSERT(destFilename.length() < sizeof(entry.destFilename));
267 (void) Fw::StringUtils::string_copy(entry.srcFilename, sourceFilename.toChar(), static_cast<FwSizeType>(sizeof(entry.srcFilename)));
268 (void) Fw::StringUtils::string_copy(entry.destFilename, destFilename.toChar(), static_cast<FwSizeType>(sizeof(entry.destFilename)));
269
270 Os::Queue::Status status = m_fileQueue.send(reinterpret_cast<U8*>(&entry), static_cast<FwSizeType>(sizeof(entry)), 0, Os::Queue::BlockingType::NONBLOCKING);
271
272 if(status != Os::Queue::Status::OP_OK) {
273 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
274 }
275 }
276
277 void FileDownlink ::
278 Cancel_cmdHandler(
279 const FwOpcodeType opCode,
280 const U32 cmdSeq
281 )
282 {
283 //Must be able to cancel in both downlink and waiting states
284 if (this->m_mode.get() == Mode::DOWNLINK || this->m_mode.get() == Mode::WAIT) {
285 this->m_mode.set(Mode::CANCEL);
286 }
287 this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
288 }
289
290 // ----------------------------------------------------------------------
291 // Private helper methods
292 // ----------------------------------------------------------------------
293
294 Fw::CmdResponse FileDownlink ::
295 statusToCmdResp(SendFileStatus status)
296 {
297 switch(status.e) {
299 return Fw::CmdResponse::OK;
306 default:
307 // Trigger assertion if given unknown status
308 FW_ASSERT(false);
309 }
310
311 // It's impossible to reach this, but added to suppress gcc missing return warning
313 }
314
315 void FileDownlink ::
316 sendResponse(SendFileStatus resp)
317 {
318 if(this->m_curEntry.source == FileDownlink::COMMAND) {
319 this->cmdResponse_out(this->m_curEntry.opCode, this->m_curEntry.cmdSeq, statusToCmdResp(resp));
320 } else {
321 for(NATIVE_INT_TYPE i = 0; i < this->getNum_FileComplete_OutputPorts(); i++) {
322 if(this->isConnected_FileComplete_OutputPort(i)) {
323 this->FileComplete_out(i, Svc::SendFileResponse(resp, this->m_curEntry.context));
324 }
325 }
326 }
327 }
328
329 void FileDownlink ::
330 sendFile(
331 const char* sourceFilename,
332 const char* destFilename,
333 U32 startOffset,
334 U32 length
335 )
336 {
337 // Open file for downlink
338 Os::File::Status status = this->m_file.open(
339 sourceFilename,
340 destFilename
341 );
342
343 // Reject command if error when opening file
344 if (status != Os::File::OP_OK) {
345 this->m_mode.set(Mode::IDLE);
346 this->m_warnings.fileOpenError();
347 sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK : SendFileStatus::STATUS_ERROR);
348 return;
349 }
350
351
352 if (startOffset >= this->m_file.getSize()) {
353 this->enterCooldown();
354 this->log_WARNING_HI_DownlinkPartialFail(this->m_file.getSourceName(), this->m_file.getDestName(), startOffset, this->m_file.getSize());
355 sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK : SendFileStatus::STATUS_INVALID);
356 return;
357 } else if (startOffset + length > this->m_file.getSize()) {
358 // If the amount to downlink is greater than the file size, emit a Warning and then allow
359 // the file to be downlinked anyway
360 this->log_WARNING_LO_DownlinkPartialWarning(startOffset, length, this->m_file.getSize(), this->m_file.getSourceName(), this->m_file.getDestName());
361 length = this->m_file.getSize() - startOffset;
362 }
363
364 // Send file and switch to WAIT mode
365 this->getBuffer(this->m_buffer, FILE_PACKET);
366 this->sendStartPacket();
367 this->m_mode.set(Mode::WAIT);
368 this->m_sequenceIndex = 1;
369 this->m_curTimer = 0;
370 this->m_byteOffset = startOffset;
371 this->m_lastCompletedType = Fw::FilePacket::T_START;
372
373 // zero length means read until end of file
374 if (length > 0) {
375 this->log_ACTIVITY_HI_SendStarted(length, this->m_file.getSourceName(), this->m_file.getDestName());
376 this->m_endOffset = startOffset + length;
377 }
378 else {
379 this->log_ACTIVITY_HI_SendStarted(this->m_file.getSize() - startOffset, this->m_file.getSourceName(), this->m_file.getDestName());
380 this->m_endOffset = this->m_file.getSize();
381 }
382 }
383
384 Os::File::Status FileDownlink ::
385 sendDataPacket(U32 &byteOffset)
386 {
387 FW_ASSERT(byteOffset < this->m_endOffset);
389 const U32 dataSize = (byteOffset + maxDataSize > this->m_endOffset) ? (this->m_endOffset - byteOffset) : maxDataSize;
391 //This will be last data packet sent
392 if (dataSize + byteOffset == this->m_endOffset) {
393 this->m_lastCompletedType = Fw::FilePacket::T_DATA;
394 }
395
396 const Os::File::Status status =
397 this->m_file.read(buffer, byteOffset, dataSize);
398 if (status != Os::File::OP_OK) {
399 this->m_warnings.fileRead(status);
400 return status;
401 }
402
404 dataPacket.initialize(
405 this->m_sequenceIndex,
406 byteOffset,
407 static_cast<U16>(dataSize),
408 buffer);
409 ++this->m_sequenceIndex;
410 Fw::FilePacket filePacket;
411 filePacket.fromDataPacket(dataPacket);
412 this->sendFilePacket(filePacket);
413
414 byteOffset += dataSize;
415
416 return Os::File::OP_OK;
417
418 }
419
420 void FileDownlink ::
421 sendCancelPacket()
422 {
423 Fw::Buffer buffer;
424 Fw::FilePacket::CancelPacket cancelPacket;
425 cancelPacket.initialize(this->m_sequenceIndex);
426
427 Fw::FilePacket filePacket;
428 filePacket.fromCancelPacket(cancelPacket);
429 this->getBuffer(buffer, CANCEL_PACKET);
430
431 const Fw::SerializeStatus status = filePacket.toBuffer(buffer);
433 this->bufferSendOut_out(0, buffer);
434 this->m_packetsSent.packetSent();
435 }
436
437 void FileDownlink ::
438 sendEndPacket()
439 {
440 CFDP::Checksum checksum;
441 this->m_file.getChecksum(checksum);
442
444 endPacket.initialize(this->m_sequenceIndex, checksum);
445
446 Fw::FilePacket filePacket;
447 filePacket.fromEndPacket(endPacket);
448 this->sendFilePacket(filePacket);
449
450 }
451
452 void FileDownlink ::
453 sendStartPacket()
454 {
455 Fw::FilePacket::StartPacket startPacket;
456 startPacket.initialize(
457 this->m_file.getSize(),
458 this->m_file.getSourceName().toChar(),
459 this->m_file.getDestName().toChar()
460 );
461 Fw::FilePacket filePacket;
462 filePacket.fromStartPacket(startPacket);
463 this->sendFilePacket(filePacket);
464 }
465
466 void FileDownlink ::
467 sendFilePacket(const Fw::FilePacket& filePacket)
468 {
469 const U32 bufferSize = filePacket.bufferSize();
470 FW_ASSERT(this->m_buffer.getData() != nullptr);
471 FW_ASSERT(
472 this->m_buffer.getSize() >= bufferSize,
473 static_cast<FwAssertArgType>(bufferSize),
474 static_cast<FwAssertArgType>(this->m_buffer.getSize()));
475 const Fw::SerializeStatus status = filePacket.toBuffer(this->m_buffer);
477 // set the buffer size to the packet size
478 this->m_buffer.setSize(bufferSize);
479 this->bufferSendOut_out(0, this->m_buffer);
480 // restore buffer size to max
481 this->m_buffer.setSize(FILEDOWNLINK_INTERNAL_BUFFER_SIZE);
482 this->m_packetsSent.packetSent();
483 }
484
485 void FileDownlink ::
486 enterCooldown()
487 {
488 this->m_file.getOsFile().close();
489 this->m_mode.set(Mode::COOLDOWN);
490 this->m_lastCompletedType = Fw::FilePacket::T_NONE;
491 this->m_curTimer = 0;
492 }
493
494 void FileDownlink ::
495 downlinkPacket()
496 {
497 FW_ASSERT(this->m_lastCompletedType != Fw::FilePacket::T_NONE, this->m_lastCompletedType);
498 FW_ASSERT(this->m_mode.get() == Mode::CANCEL || this->m_mode.get() == Mode::DOWNLINK, this->m_mode.get());
499 //If canceled mode and currently downlinking data then send a cancel packet
500 if (this->m_mode.get() == Mode::CANCEL && this->m_lastCompletedType == Fw::FilePacket::T_START) {
501 this->sendCancelPacket();
502 this->m_lastCompletedType = Fw::FilePacket::T_CANCEL;
503 }
504 //If in downlink mode and currently downlinking data then continue with the next packer
505 else if (this->m_mode.get() == Mode::DOWNLINK && this->m_lastCompletedType == Fw::FilePacket::T_START) {
506 //Send the next packet, or fail doing so
507 const Os::File::Status status = this->sendDataPacket(this->m_byteOffset);
508 if (status != Os::File::OP_OK) {
509 this->log_WARNING_HI_SendDataFail(this->m_file.getSourceName(), this->m_byteOffset);
510 this->enterCooldown();
511 this->sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK : SendFileStatus::STATUS_ERROR);
512 //Don't go to wait state
513 return;
514 }
515 }
516 //If in downlink mode or cancel and finished downlinking data then send the last packet
517 else if (this->m_lastCompletedType == Fw::FilePacket::T_DATA) {
518 this->sendEndPacket();
519 this->m_lastCompletedType = Fw::FilePacket::T_END;
520 }
521 this->m_mode.set(Mode::WAIT);
522 this->m_curTimer = 0;
523 }
524
525 void FileDownlink ::
526 finishHelper(bool cancel)
527 {
528 //Complete command and switch to IDLE
529 if (not cancel) {
530 this->m_filesSent.fileSent();
531 this->log_ACTIVITY_HI_FileSent(this->m_file.getSourceName(), this->m_file.getDestName());
532 } else {
533 this->log_ACTIVITY_HI_DownlinkCanceled(this->m_file.getSourceName(), this->m_file.getDestName());
534 }
535 this->enterCooldown();
536 sendResponse(SendFileStatus::STATUS_OK);
537 }
538
539 void FileDownlink ::
540 getBuffer(Fw::Buffer& buffer, PacketType type)
541 {
542 //Check type is correct
543 FW_ASSERT(type < COUNT_PACKET_TYPE && type >= 0, type);
544 // Wrap the buffer around our indexed memory.
545 buffer.setData(this->m_memoryStore[type]);
547 //Set a known ID to look for later
548 buffer.setContext(m_lastBufferId);
549 m_lastBufferId++;
550 }
551} // end namespace Svc
#define FW_ASSERT(...)
Definition Assert.hpp:14
PlatformIntType NATIVE_INT_TYPE
Definition BasicTypes.h:55
uint8_t U8
8-bit unsigned integer
Definition BasicTypes.h:30
PlatformAssertArgType FwAssertArgType
Definition FpConfig.h:39
U32 FwOpcodeType
Definition FpConfig.h:91
PlatformSizeType FwSizeType
Definition FpConfig.h:35
PlatformQueuePriorityType FwQueuePriorityType
Definition FpConfig.h:55
C++-compatible configuration header for fprime configuration.
Class representing a 32-bit checksum as mandated by the CCSDS File Delivery Protocol.
Definition Checksum.hpp:53
void setData(U8 *data)
Definition Buffer.cpp:80
void setContext(U32 context)
Definition Buffer.cpp:94
void setSize(U32 size)
Definition Buffer.cpp:87
U32 getContext() const
Definition Buffer.cpp:76
Enum representing a command response.
@ EXECUTION_ERROR
Command had execution error.
@ VALIDATION_ERROR
Command failed validation.
@ OK
Command successfully executed.
@ BUSY
Component busy.
const char * toChar() const
Definition CmdString.hpp:50
The type of a cancel packet.
void initialize(const U32 sequenceIndex)
Initialize a cancel packet.
The type of a data packet.
void initialize(const U32 sequenceIndex, const U32 byteOffset, const U16 dataSize, const U8 *const data)
Initialize a data packet.
The type of an end packet.
void initialize(const U32 sequenceIndex, const CFDP::Checksum &checksum)
Initialize an end packet.
Definition EndPacket.cpp:21
SizeType length() const
Get length of string.
virtual const CHAR * toChar() const =0
@ OP_OK
Operation was successful.
Definition File.hpp:30
@ NONBLOCKING
Message will return with status when space is unavailable.
Definition Queue.hpp:46
Status
status returned from the queue send function
Definition Queue.hpp:30
@ OP_OK
message sent/received okay
Definition Queue.hpp:31
char * string_copy(char *destination, const char *source, FwSizeType num)
copy string with null-termination guaranteed
SerializeStatus
forward declaration for string
@ FW_SERIALIZE_OK
Serialization/Deserialization operation was successful.
static const bool FILEDOWNLINK_COMMAND_FAILURES_DISABLED
static const U32 FILEDOWNLINK_INTERNAL_BUFFER_SIZE
The type of a start packet.
void initialize(const U32 fileSize, const char *const sourcePath, const char *const destinationPath)
Initialize a StartPacket with sequence number 0.
A file packet.
void fromCancelPacket(const CancelPacket &cancelPacket)
void fromEndPacket(const EndPacket &endPacket)
void fromDataPacket(const DataPacket &dataPacket)
void fromStartPacket(const StartPacket &startPacket)
U32 bufferSize() const
SerializeStatus toBuffer(Buffer &buffer) const