Commit 989cdcd0 authored by Lorenz Meier's avatar Lorenz Meier

Merge pull request #818 from DonLakeFlyer/FTPProgressBar

FTP progress bar and download speed
parents 0cda5053 5868dbc1
......@@ -59,15 +59,17 @@ void QGCUASFileManagerUnitTest::init(void)
Q_ASSERT(connected);
Q_UNUSED(connected); // Silent release build compiler warning
connected = connect(_fileManager, SIGNAL(statusMessage(const QString&)), this, SLOT(statusMessage(const QString&)));
connected = connect(_fileManager, SIGNAL(listEntry(const QString&)), this, SLOT(listEntry(const QString&)));
Q_ASSERT(connected);
_rgSignals[statusMessageSignalIndex] = SIGNAL(statusMessage(const QString&));
_rgSignals[errorMessageSignalIndex] = SIGNAL(errorMessage(const QString&));
_rgSignals[resetStatusMessagesSignalIndex] = SIGNAL(resetStatusMessages(void));
_rgSignals[listEntrySignalIndex] = SIGNAL(listEntry(const QString&));
_rgSignals[listCompleteSignalIndex] = SIGNAL(listComplete(void));
_rgSignals[openFileLengthSignalIndex] = SIGNAL(openFileLength(unsigned int));
_rgSignals[downloadFileLengthSignalIndex] = SIGNAL(downloadFileLength(unsigned int));
_rgSignals[downloadFileCompleteSignalIndex] = SIGNAL(downloadFileComplete(void));
_rgSignals[errorMessageSignalIndex] = SIGNAL(errorMessage(const QString&));
_multiSpy = new MultiSignalSpy();
Q_CHECK_PTR(_multiSpy);
QCOMPARE(_multiSpy->init(_fileManager, _rgSignals, _cSignals), true);
......@@ -86,11 +88,11 @@ void QGCUASFileManagerUnitTest::cleanup(void)
_multiSpy = NULL;
}
/// @brief Connected to QGCUASFileManager statusMessage signal in order to catch list command output
void QGCUASFileManagerUnitTest::statusMessage(const QString& msg)
/// @brief Connected to QGCUASFileManager listEntry signal in order to catch list entries
void QGCUASFileManagerUnitTest::listEntry(const QString& entry)
{
// Keep a list of all names received so we can test it for correctness
_fileListReceived += msg;
_fileListReceived += entry;
}
......@@ -151,14 +153,13 @@ void QGCUASFileManagerUnitTest::_listTest(void)
Q_ASSERT(_multiSpy->checkNoSignals() == true);
// QGCUASFileManager::listDirectory signalling as follows:
// Emits the resetStatusMessages signal
// Emits a statusMessage signal for each list entry
// Emits a listEntry signal for each list entry
// Emits an errorMessage signal if:
// It gets a Nak back
// Sequence number is incorrrect on any response
// CRC is incorrect on any responses
// List entry is formatted incorrectly
// It is possible to get a number of good statusMessage signals, followed by an errorMessage signal
// It is possible to get a number of good listEntry signals, followed by an errorMessage signal
// Emits listComplete after it receives the final list entry
// If an errorMessage signal is signalled no listComplete is signalled
......@@ -166,7 +167,7 @@ void QGCUASFileManagerUnitTest::_listTest(void)
// We should get a single resetStatusMessages signal
// We should get a single errorMessage signal
_fileManager->listDirectory("/bogus");
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask | resetStatusMessagesSignalMask), true);
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask), true);
_multiSpy->clearAllSignals();
// Setup the mock file server with a valid directory list
......@@ -187,15 +188,15 @@ void QGCUASFileManagerUnitTest::_listTest(void)
// For simulated server errors on subsequent Acks, the first Ack will go through. This means we should have gotten some
// partial results. In the case of the directory list test set, all entries fit into the first ack, so we should have
// gotten back all of them.
QCOMPARE(_multiSpy->getSpyByIndex(statusMessageSignalIndex)->count(), fileList.count());
_multiSpy->clearSignalByIndex(statusMessageSignalIndex);
QCOMPARE(_multiSpy->getSpyByIndex(listEntrySignalIndex)->count(), fileList.count());
_multiSpy->clearSignalByIndex(listEntrySignalIndex);
// And then it should have errored out because the next list Request would have failed.
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask | resetStatusMessagesSignalMask), true);
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask), true);
} else {
// For the simulated errors which failed the intial response we should not have gotten any results back at all.
// Just an error.
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask | resetStatusMessagesSignalMask), true);
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask), true);
}
// Set everything back to initial state
......@@ -206,9 +207,9 @@ void QGCUASFileManagerUnitTest::_listTest(void)
// Send a list command at the root of the directory tree which should succeed
_fileManager->listDirectory("/");
QCOMPARE(_multiSpy->checkSignalByMask(resetStatusMessagesSignalMask | listCompleteSignalMask), true);
QCOMPARE(_multiSpy->checkSignalByMask(listCompleteSignalMask), true);
QCOMPARE(_multiSpy->checkNoSignalByMask(errorMessageSignalMask), true);
QCOMPARE(_multiSpy->getSpyByIndex(statusMessageSignalIndex)->count(), fileList.count());
QCOMPARE(_multiSpy->getSpyByIndex(listEntrySignalIndex)->count(), fileList.count());
QVERIFY(_fileListReceived == fileList);
}
......@@ -238,26 +239,26 @@ void QGCUASFileManagerUnitTest::_downloadTest(void)
Q_ASSERT(_multiSpy->checkNoSignals() == true);
// QGCUASFileManager::downloadPath works as follows:
// Emits the resetStatusMessages signal
// Sends an Open Command to the server
// Expects an Ack Response back from the server with the correct sequence numner
// Emits an errorMessage signal if it gets a Nak back
// Emits an openFileLength signal with the file length if it gets back a good Ack
// Emits an downloadFileLength signal with the file length if it gets back a good Ack
// Sends subsequent Read commands to the server until it gets the full file contents back
// Emits a downloadFileProgress for each read command ack it gets back
// Sends Terminate command to server when download is complete to close Open command
// Mock file server will signal terminateCommandReceived when it gets a Terminate command
// Sends statusMessage signal to indicate the download is complete
// Sends downloadFileComplete signal to indicate the download is complete
// Emits an errorMessage signal if sequence number is incorrrect on any response
// Emits an errorMessage signal if CRC is incorrect on any responses
// Expected signals if the Open command fails for any reason
quint16 signalMaskOpenFailure = resetStatusMessagesSignalMask | errorMessageSignalMask;
quint16 signalMaskOpenFailure = errorMessageSignalMask;
// Expected signals if the Read command fails for any reason
quint16 signalMaskReadFailure = resetStatusMessagesSignalMask | openFileLengthSignalMask | errorMessageSignalMask;
quint16 signalMaskReadFailure = downloadFileLengthSignalMask | errorMessageSignalMask;
// Expected signals if the downloadPath command succeeds
quint16 signalMaskDownloadSuccess = resetStatusMessagesSignalMask | openFileLengthSignalMask | statusMessageSignalMask;
quint16 signalMaskDownloadSuccess = downloadFileLengthSignalMask | downloadFileCompleteSignalMask;
// Send a bogus path
// We should get a single resetStatusMessages signal
......@@ -296,7 +297,8 @@ void QGCUASFileManagerUnitTest::_downloadTest(void)
// For simulated server errors on subsequent Acks, the first Ack will go through. We must handle things differently depending
// on whether the downloaded file requires multiple packets to complete the download.
if (testCase->fMultiPacketResponse) {
// The downloaded file requires multiple Acks to complete. Hence second Read should have failed.
// The downloaded file requires multiple Acks to complete. Hence first Read should have succeeded and sent one downloadFileComplete.
// Second Read should have failed.
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskReadFailure), true);
// Open command succeeded, so we should get a Terminate for the open
......@@ -333,7 +335,7 @@ void QGCUASFileManagerUnitTest::_downloadTest(void)
QCOMPARE(_multiSpy->checkOnlySignalByMask(signalMaskDownloadSuccess), true);
// Make sure the file length coming back through the openFileLength signal is correct
QVERIFY(_multiSpy->getSpyByIndex(openFileLengthSignalIndex)->takeFirst().at(0).toInt() == testCase->length);
QVERIFY(_multiSpy->getSpyByIndex(downloadFileLengthSignalIndex)->takeFirst().at(0).toInt() == testCase->length);
_multiSpy->clearAllSignals();
......
......@@ -58,29 +58,28 @@ private slots:
void _listTest(void);
void _downloadTest(void);
// Connected to QGCUASFileManager statusMessage signal
void statusMessage(const QString&);
// Connected to QGCUASFileManager listEntry signal
void listEntry(const QString& entry);
private:
void _validateFileContents(const QString& filePath, uint8_t length);
enum {
statusMessageSignalIndex = 0,
errorMessageSignalIndex,
resetStatusMessagesSignalIndex,
listEntrySignalIndex = 0,
listCompleteSignalIndex,
openFileLengthSignalIndex,
downloadFileLengthSignalIndex,
downloadFileCompleteSignalIndex,
errorMessageSignalIndex,
maxSignalIndex
};
enum {
statusMessageSignalMask = 1 << statusMessageSignalIndex,
errorMessageSignalMask = 1 << errorMessageSignalIndex,
resetStatusMessagesSignalMask = 1 << resetStatusMessagesSignalIndex,
listEntrySignalMask = 1 << listEntrySignalIndex,
listCompleteSignalMask = 1 << listCompleteSignalIndex,
openFileLengthSignalMask = 1 << openFileLengthSignalIndex,
downloadFileLengthSignalMask = 1 << downloadFileLengthSignalIndex,
downloadFileCompleteSignalMask = 1 << downloadFileCompleteSignalIndex,
errorMessageSignalMask = 1 << errorMessageSignalIndex,
};
MockUAS _mockUAS;
MockMavlinkFileServer _mockFileServer;
......
......@@ -107,7 +107,9 @@ void QGCUASFileManager::_openAckResponse(Request* openAck)
// File length comes back in data
Q_ASSERT(openAck->hdr.size == sizeof(uint32_t));
emit openFileLength(openAck->openFileLength);
emit downloadFileLength(openAck->openFileLength);
// Start the sequence of read commands
_readOffset = 0; // Start reading at beginning of file
_readFileAccumulator.clear(); // Start with an empty file
......@@ -142,7 +144,7 @@ void QGCUASFileManager::_closeReadSession(bool success)
}
file.close();
_emitStatusMessage(tr("Download complete '%1'").arg(downloadFilePath));
emit downloadFileComplete();
}
// Close the open session
......@@ -167,6 +169,7 @@ void QGCUASFileManager::_readAckResponse(Request* readAck)
}
_readFileAccumulator.append((const char*)readAck->data, readAck->hdr.size);
emit downloadFileProgress(_readFileAccumulator.length());
if (readAck->hdr.size == sizeof(readAck->data)) {
// Possibly still more data to read, send next read request
......@@ -221,7 +224,9 @@ void QGCUASFileManager::_listAckResponse(Request* listAck)
// Returned names are prepended with D for directory, F for file, U for unknown
if (*ptr == 'F' || *ptr == 'D') {
// put it in the view
_emitStatusMessage(ptr);
_emitListEntry(ptr);
} else {
qDebug() << "unknown entry" << ptr;
}
// account for the name + NUL
......@@ -357,9 +362,6 @@ void QGCUASFileManager::listDirectory(const QString& dirPath)
return;
}
// clear the text widget
emit resetStatusMessages();
// initialise the lister
_listPath = dirPath;
_listOffset = 0;
......@@ -411,8 +413,6 @@ void QGCUASFileManager::downloadPath(const QString& from, const QDir& downloadDi
i++; // move past slash
_readFileDownloadFilename = from.right(from.size() - i);
emit resetStatusMessages();
_currentOperation = kCOOpen;
Request request;
......@@ -532,10 +532,10 @@ void QGCUASFileManager::_emitErrorMessage(const QString& msg)
emit errorMessage(msg);
}
void QGCUASFileManager::_emitStatusMessage(const QString& msg)
void QGCUASFileManager::_emitListEntry(const QString& entry)
{
qDebug() << "QGCUASFileManager: Status" << msg;
emit statusMessage(msg);
qDebug() << "QGCUASFileManager: list entry" << entry;
emit listEntry(entry);
}
/// @brief Sends the specified Request out to the UAS.
......
......@@ -45,11 +45,28 @@ public:
static const int ackTimerTimeoutMsecs = 1000;
signals:
void statusMessage(const QString& msg);
void resetStatusMessages();
/// @brief Signalled whenever an error occurs during the listDirectory or downloadPath methods.
void errorMessage(const QString& msg);
// Signals associated with the listDirectory method
/// @brief Signalled to indicate a new directory entry was received.
void listEntry(const QString& entry);
/// @brief Signalled after listDirectory completes. If an error occurs during directory listing this signal will not be emitted.
void listComplete(void);
void openFileLength(unsigned int length);
// Signals associated with the downloadPath method
/// @brief Signalled after downloadPath is called to indicate length of file being downloaded
void downloadFileLength(unsigned int length);
/// @brief Signalled during file download to indicate download progress
/// @param bytesReceived Number of bytes currently received from file
void downloadFileProgress(unsigned int bytesReceived);
/// @brief Signaled to indicate completion of file download. If an error occurs during download this signal will not be emitted.
void downloadFileComplete(void);
public slots:
void receiveMessage(LinkInterface* link, mavlink_message_t message);
......@@ -140,7 +157,7 @@ protected:
void _setupAckTimeout(void);
void _clearAckTimeout(void);
void _emitErrorMessage(const QString& msg);
void _emitStatusMessage(const QString& msg);
void _emitListEntry(const QString& entry);
void _sendRequest(Request* request);
void _fillRequestWithString(Request* request, const QString& str);
void _openAckResponse(Request* openAck);
......
This diff is collapsed.
......@@ -42,29 +42,38 @@ protected:
private slots:
void _refreshTree(void);
void _downloadFiles(void);
void _treeStatusMessage(const QString& msg);
void _treeErrorMessage(const QString& msg);
void _listEntryReceived(const QString& entry);
void _listErrorMessage(const QString& msg);
void _listComplete(void);
void _downloadStatusMessage(const QString& msg);
void _downloadFile(void);
void _downloadLength(unsigned int length);
void _downloadProgress(unsigned int length);
void _downloadErrorMessage(const QString& msg);
void _downloadComplete(void);
void _currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous);
void _listCompleteTimeout(void);
private:
void _setupListCompleteTimeout(void);
void _clearListCompleteTimeout(void);
void _connectDownloadSignals(void);
void _disconnectDownloadSignals(void);
void _connectListSignals(void);
void _disconnectListSignals(void);
void _requestDirectoryList(const QString& dir);
static const int _typeFile = QTreeWidgetItem::UserType + 1;
static const int _typeDir = QTreeWidgetItem::UserType + 2;
static const int _typeError = QTreeWidgetItem::UserType + 3;
QTimer _listCompleteTimer; ///> Used to signal a timeout waiting for a listComplete signal
static const int _listCompleteTimerTimeoutMsecs = 5000; ///> Timeout in msecs for listComplete timer
QList<int> _walkIndexStack;
QList<QTreeWidgetItem*> _walkItemStack;
Ui::QGCUASFileView _ui;
QString _downloadFilename; ///< File currently being downloaded, not including path
QTime _downloadStartTime; ///< Time at which download started
bool _listInProgress; ///< Indicates that a listDirectory command is in progress
bool _downloadInProgress; ///< Indicates that a downloadPath command is in progress
};
#endif // QGCUASFILEVIEW_H
......@@ -14,13 +14,23 @@
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<item row="3" column="1">
<widget class="QPushButton" name="listFilesButton">
<property name="text">
<string>List Files</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QTreeWidget" name="treeWidget">
<property name="headerHidden">
......@@ -33,7 +43,7 @@
</column>
</widget>
</item>
<item row="1" column="2">
<item row="3" column="2">
<widget class="QPushButton" name="downloadButton">
<property name="enabled">
<bool>false</bool>
......@@ -43,6 +53,19 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QLabel" name="statusText">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment