F´ Flight Software - C/C++ Documentation devel
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 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 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->m_timeout = timeout;
64 this->m_cooldown = cooldown;
65 this->m_cycleTime = cycleTime;
66 this->m_configured = true;
67
68 Os::Queue::QueueStatus stat = m_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->m_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->m_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 = m_fileQueue.receive(
104 reinterpret_cast<U8*>(&this->m_curEntry),
105 sizeof(this->m_curEntry),
106 real_size,
107 prio,
109 );
110
111 if(stat != Os::Queue::QUEUE_OK || sizeof(this->m_curEntry) != real_size) {
112 return;
113 }
114
115 sendFile(
116 this->m_curEntry.srcFilename,
117 this->m_curEntry.destFilename,
118 this->m_curEntry.offset,
119 this->m_curEntry.length
120 );
121 break;
122 }
123 case Mode::COOLDOWN: {
124 if (this->m_curTimer >= this->m_cooldown) {
125 this->m_curTimer = 0;
126 this->m_mode.set(Mode::IDLE);
127 } else {
128 this->m_curTimer += m_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->m_curTimer >= this->m_timeout) {
135 this->m_curTimer = 0;
136 this->log_WARNING_HI_DownlinkTimeout(this->m_file.getSourceName(), this->m_file.getDestName());
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->m_curTimer += m_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 = m_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 = m_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->m_lastBufferId != fwBuffer.getContext() + 1 ||
199 this->m_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->m_mode.get() == Mode::WAIT || this->m_mode.get() == Mode::CANCEL, this->m_mode.get());
204 //If the last packet has been sent (and is returning now) then finish the file
205 if (this->m_lastCompletedType == Fw::FilePacket::T_END ||
206 this->m_lastCompletedType == Fw::FilePacket::T_CANCEL) {
207 finishHelper(this->m_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->m_mode.get() == Mode::WAIT) {
212 this->m_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 = m_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 = m_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->m_mode.get() == Mode::DOWNLINK || this->m_mode.get() == Mode::WAIT) {
294 this->m_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->m_curEntry.source == FileDownlink::COMMAND) {
328 this->cmdResponse_out(this->m_curEntry.opCode, this->m_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->m_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->m_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->m_mode.set(Mode::IDLE);
355 this->m_warnings.fileOpenError();
356 sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK : SendFileStatus::STATUS_ERROR);
357 return;
358 }
359
360
361 if (startOffset >= this->m_file.getSize()) {
362 this->enterCooldown();
363 this->log_WARNING_HI_DownlinkPartialFail(this->m_file.getSourceName(), this->m_file.getDestName(), startOffset, this->m_file.getSize());
364 sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK : SendFileStatus::STATUS_INVALID);
365 return;
366 } else if (startOffset + length > this->m_file.getSize()) {
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->m_file.getSize(), this->m_file.getSourceName(), this->m_file.getDestName());
370 length = this->m_file.getSize() - startOffset;
371 }
372
373 // Send file and switch to WAIT mode
374 this->getBuffer(this->m_buffer, FILE_PACKET);
375 this->sendStartPacket();
376 this->m_mode.set(Mode::WAIT);
377 this->m_sequenceIndex = 1;
378 this->m_curTimer = 0;
379 this->m_byteOffset = startOffset;
380 this->m_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->m_file.getSourceName(), this->m_file.getDestName());
385 this->m_endOffset = startOffset + length;
386 }
387 else {
388 this->log_ACTIVITY_HI_SendStarted(this->m_file.getSize() - startOffset, this->m_file.getSourceName(), this->m_file.getDestName());
389 this->m_endOffset = this->m_file.getSize();
390 }
391 }
392
393 Os::File::Status FileDownlink ::
394 sendDataPacket(U32 &byteOffset)
395 {
396 FW_ASSERT(byteOffset < this->m_endOffset);
398 const U32 dataSize = (byteOffset + maxDataSize > this->m_endOffset) ? (this->m_endOffset - byteOffset) : maxDataSize;
400 //This will be last data packet sent
401 if (dataSize + byteOffset == this->m_endOffset) {
402 this->m_lastCompletedType = Fw::FilePacket::T_DATA;
403 }
404
405 const Os::File::Status status =
406 this->m_file.read(buffer, byteOffset, dataSize);
407 if (status != Os::File::OP_OK) {
408 this->m_warnings.fileRead(status);
409 return status;
410 }
411
413 dataPacket.initialize(
414 this->m_sequenceIndex,
415 byteOffset,
416 static_cast<U16>(dataSize),
417 buffer);
418 ++this->m_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 Fw::FilePacket::CancelPacket cancelPacket;
434 cancelPacket.initialize(this->m_sequenceIndex);
435
436 Fw::FilePacket filePacket;
437 filePacket.fromCancelPacket(cancelPacket);
438 this->getBuffer(buffer, CANCEL_PACKET);
439
440 const Fw::SerializeStatus status = filePacket.toBuffer(buffer);
442 this->bufferSendOut_out(0, buffer);
443 this->m_packetsSent.packetSent();
444 }
445
446 void FileDownlink ::
447 sendEndPacket()
448 {
449 CFDP::Checksum checksum;
450 this->m_file.getChecksum(checksum);
451
453 endPacket.initialize(this->m_sequenceIndex, checksum);
454
455 Fw::FilePacket filePacket;
456 filePacket.fromEndPacket(endPacket);
457 this->sendFilePacket(filePacket);
458
459 }
460
461 void FileDownlink ::
462 sendStartPacket()
463 {
464 Fw::FilePacket::StartPacket startPacket;
465 startPacket.initialize(
466 this->m_file.getSize(),
467 this->m_file.getSourceName().toChar(),
468 this->m_file.getDestName().toChar()
469 );
470 Fw::FilePacket filePacket;
471 filePacket.fromStartPacket(startPacket);
472 this->sendFilePacket(filePacket);
473 }
474
475 void FileDownlink ::
476 sendFilePacket(const Fw::FilePacket& filePacket)
477 {
478 const U32 bufferSize = filePacket.bufferSize();
479 FW_ASSERT(this->m_buffer.getData() != nullptr);
480 FW_ASSERT(this->m_buffer.getSize() >= bufferSize, bufferSize, this->m_buffer.getSize());
481 const Fw::SerializeStatus status = filePacket.toBuffer(this->m_buffer);
483 // set the buffer size to the packet size
484 this->m_buffer.setSize(bufferSize);
485 this->bufferSendOut_out(0, this->m_buffer);
486 // restore buffer size to max
487 this->m_buffer.setSize(FILEDOWNLINK_INTERNAL_BUFFER_SIZE);
488 this->m_packetsSent.packetSent();
489 }
490
491 void FileDownlink ::
492 enterCooldown()
493 {
494 this->m_file.getOsFile().close();
495 this->m_mode.set(Mode::COOLDOWN);
496 this->m_lastCompletedType = Fw::FilePacket::T_NONE;
497 this->m_curTimer = 0;
498 }
499
500 void FileDownlink ::
501 downlinkPacket()
502 {
503 FW_ASSERT(this->m_lastCompletedType != Fw::FilePacket::T_NONE, this->m_lastCompletedType);
504 FW_ASSERT(this->m_mode.get() == Mode::CANCEL || this->m_mode.get() == Mode::DOWNLINK, this->m_mode.get());
505 //If canceled mode and currently downlinking data then send a cancel packet
506 if (this->m_mode.get() == Mode::CANCEL && this->m_lastCompletedType == Fw::FilePacket::T_START) {
507 this->sendCancelPacket();
508 this->m_lastCompletedType = Fw::FilePacket::T_CANCEL;
509 }
510 //If in downlink mode and currently downlinking data then continue with the next packer
511 else if (this->m_mode.get() == Mode::DOWNLINK && this->m_lastCompletedType == Fw::FilePacket::T_START) {
512 //Send the next packet, or fail doing so
513 const Os::File::Status status = this->sendDataPacket(this->m_byteOffset);
514 if (status != Os::File::OP_OK) {
515 this->log_WARNING_HI_SendDataFail(this->m_file.getSourceName(), this->m_byteOffset);
516 this->enterCooldown();
517 this->sendResponse(FILEDOWNLINK_COMMAND_FAILURES_DISABLED ? SendFileStatus::STATUS_OK : SendFileStatus::STATUS_ERROR);
518 //Don't go to wait state
519 return;
520 }
521 }
522 //If in downlink mode or cancel and finished downlinking data then send the last packet
523 else if (this->m_lastCompletedType == Fw::FilePacket::T_DATA) {
524 this->sendEndPacket();
525 this->m_lastCompletedType = Fw::FilePacket::T_END;
526 }
527 this->m_mode.set(Mode::WAIT);
528 this->m_curTimer = 0;
529 }
530
531 void FileDownlink ::
532 finishHelper(bool cancel)
533 {
534 //Complete command and switch to IDLE
535 if (not cancel) {
536 this->m_filesSent.fileSent();
537 this->log_ACTIVITY_HI_FileSent(this->m_file.getSourceName(), this->m_file.getDestName());
538 } else {
539 this->log_ACTIVITY_HI_DownlinkCanceled(this->m_file.getSourceName(), this->m_file.getDestName());
540 }
541 this->enterCooldown();
542 sendResponse(SendFileStatus::STATUS_OK);
543 }
544
545 void FileDownlink ::
546 getBuffer(Fw::Buffer& buffer, PacketType type)
547 {
548 //Check type is correct
549 FW_ASSERT(type < COUNT_PACKET_TYPE && type >= 0, type);
550 // Wrap the buffer around our indexed memory.
551 buffer.setData(this->m_memoryStore[type]);
553 //Set a known ID to look for later
554 buffer.setContext(m_lastBufferId);
555 m_lastBufferId++;
556 }
557} // end namespace Svc
#define FW_ASSERT(...)
Definition Assert.hpp:14
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
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.
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
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