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