F´ Flight Software - C/C++ Documentation devel
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 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