summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenis Shienkov <denis.shienkov@gmail.com>2012-12-05 15:15:26 +0400
committerDenis Shienkov <denis.shienkov@gmail.com>2012-12-13 08:41:54 +0100
commit097eb2eeb612ffcce9dfc0092f5486feeafd649c (patch)
tree1138b2bf0fab1b45c7ac073af03bc55cc910150a
parent125a2bb81b15eb0c80c7e0aa8bf0d66d67489892 (diff)
Added documentation for BlockingMaster example
Added documentation for BlockingMaster example and added bookmarks, used in documentation to reffer to code. Change-Id: I923d27a51d46b17eaf478c0280823c8ce9b7ea3e Reviewed-by: Denis Shienkov <denis.shienkov@gmail.com>
-rw-r--r--doc/src/examples/blockingmaster.qdoc162
-rw-r--r--examples/blockingmaster/masterthread.cpp23
-rw-r--r--examples/blockingmaster/masterthread.h2
3 files changed, 181 insertions, 6 deletions
diff --git a/doc/src/examples/blockingmaster.qdoc b/doc/src/examples/blockingmaster.qdoc
new file mode 100644
index 00000000..db1ad96f
--- /dev/null
+++ b/doc/src/examples/blockingmaster.qdoc
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2011-2012 Denis Shienkov <scapig2@yandex.ru>
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtSerialPort module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** GNU Free Documentation License
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms
+** and conditions contained in a signed written agreement between you
+** and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \module QtAddonSerialPort
+ \title Blocking Master example
+ \example examples/blockingmaster
+
+ The Blocking Master example shows how to create a application for a
+ serial interface using SerialPort's synchronous API in a non-GUI thread.
+
+ \image blockingmaster-example.png Screenshot of the Blocking Master example
+
+ SerialPort supports two general programming approaches:
+
+ \list
+
+ \o \i{The asynchronous (non-blocking) approach.} Operations are scheduled
+ and performed when the control returns to Qt's event loop. SerialPort emits
+ a signal when the operation is finished. For example, SerialPort::write()
+ returns immediately. When the data is sent to the serial port, SerialPort
+ emits \l{SerialPort::bytesWritten()}{bytesWritten()}.
+
+ \o \i{The synchronous (blocking) approach.} In non-GUI and multithreaded
+ applications, the \c waitFor...() functions can be called (i.e.
+ SerialPort::waitReadyRead()) to suspend the calling thread until the
+ operation has completed.
+
+ \endlist
+
+ In this example, the synchronous approach is demonstrated. The
+ \l{examples/terminal}{Simple Terminal} example illustrates the
+ asynchronous approach.
+
+ The purpose of this example is to demonstrate a pattern that you can use
+ to simplify your serial programming code, without losing responsiveness
+ in your user interface. Use of Qt's blocking serial programming API often
+ leads to simpler code, but because of its blocking behavior, it should only
+ be used in non-GUI threads to prevent the user interface from freezing.
+ But contrary to what many think, using threads with QThread does not
+ necessarily add unmanagable complexity to your application.
+
+ This application is a Master, that demonstrate the work paired with Slave
+ application \l{examples/blockingslave}{Blocking Slave Example}.
+
+ The Master application is initiate the transfer request via serial port to
+ the Slave application and wait for a response from it.
+
+ We will start with the MasterThread class, which handles the serial
+ programming code.
+
+ \snippet examples/blockingmaster/masterthread.h 0
+
+ MasterThread is a QThread subclass that provides an API for scheduling
+ requests to Slave, and it has signals for delivering responses and reporting
+ errors. You can call transaction() to startup new master transaction with
+ desired request data and other parameters, and the result is delivered by
+ the response() signal. If any error occurs, the error() or timeout() signals
+ is emitted.
+
+ It's important to notice that transaction() is called from the main, GUI
+ thread, but the request data and other parameters will be accessed from
+ MasterThread's thread. Because we will be reading and writing MasterThread's
+ data members from different threads concurrently, we use QMutex to
+ synchronize access.
+
+ \snippet examples/blockingmaster/masterthread.cpp 2
+
+ The transaction() function stores the serial port name, timeout and request
+ data, and we lock the mutex with QMutexLocker to protect this data. We then
+ start the thread, unless it is already running. We will come back to the
+ QWaitCondition::wakeOne() call later.
+
+ \snippet examples/blockingmaster/masterthread.cpp 4
+ \snippet examples/blockingmaster/masterthread.cpp 5
+
+ In the run() function, we start by acquiring the mutex lock, fetching the
+ serial port name, timeout and request data from the member data, and then
+ releasing the lock again. The case that we are protecting ourselves against
+ is that \c transaction() could be called at the same time as we are fetching
+ this data. QString is \l reentrant but \e not \l{thread-safe}, and we must
+ also avoid the unlikely risk of reading the serial port name from one request,
+ and timeout or request data of another. And as you might have guessed,
+ MasterThread can only handle one request at a time.
+
+ The SerialPort object we construct on stack into run() function before loop
+ enter:
+
+ \snippet examples/blockingmaster/masterthread.cpp 6
+
+ This allows us once to create an object, while running loop, and also means
+ that all the methods of the object will be executed in the context of the
+ run() thread.
+
+ In the loop, we check for changed or not the name of serial port for the
+ current transaction. And if the name is changed then re-open and re-configure
+ the serial port.
+
+ \snippet examples/blockingmaster/masterthread.cpp 7
+
+ The loop will continue creating request data, write to serial port and wait
+ until all data is transferred.
+
+ \snippet examples/blockingmaster/masterthread.cpp 8
+
+ \warning The method waitForBytesWritten() should be used after each write()
+ call for the blocking approach, because it processes all the I/O routines
+ instead of Qt event-loop.
+
+ The timeout() signal is emitted if error occurs when transferring data.
+
+ \snippet examples/blockingmaster/masterthread.cpp 9
+
+ After a successful request, we start wait until response and try read it.
+
+ \snippet examples/blockingmaster/masterthread.cpp 10
+
+ \warning The method waitForReadyRead() should be used before each read()
+ call for the blocking approach, because it processes all the I/O routines
+ instead of Qt event-loop.
+
+ The timeout() signal is emitted if error occurs when receiving data.
+
+ \snippet examples/blockingmaster/masterthread.cpp 11
+
+ After a successful transaction is emitted response() signal containing the
+ data received from the Slave application:
+
+ \snippet examples/blockingmaster/masterthread.cpp 12
+
+ Next, the thread goes to sleep until the next transaction is appear. On
+ waking, the thread re-reads the new data of members and run loop from the
+ beginning.
+
+ \snippet examples/blockingmaster/masterthread.cpp 13
+
+ \sa {Simple Terminal Example}, {Blocking Slave Example}
+*/
diff --git a/examples/blockingmaster/masterthread.cpp b/examples/blockingmaster/masterthread.cpp
index 6e970605..9044fdf8 100644
--- a/examples/blockingmaster/masterthread.cpp
+++ b/examples/blockingmaster/masterthread.cpp
@@ -52,6 +52,7 @@ MasterThread::MasterThread(QObject *parent)
{
}
+//! [0]
MasterThread::~MasterThread()
{
mutex.lock();
@@ -60,25 +61,31 @@ MasterThread::~MasterThread()
mutex.unlock();
wait();
}
+//! [0]
+//! [1] //! [2]
void MasterThread::transaction(const QString &portName, int waitTimeout, const QString &request)
{
+ //! [1]
QMutexLocker locker(&mutex);
this->portName = portName;
this->waitTimeout = waitTimeout;
this->request = request;
-
+ //! [3]
if (!isRunning())
start();
else
cond.wakeOne();
}
+//! [2] //! [3]
+//! [4]
void MasterThread::run()
{
bool currentPortNameChanged = false;
mutex.lock();
+ //! [4] //! [5]
QString currentPortName;
if (currentPortName != portName) {
currentPortName = portName;
@@ -88,11 +95,11 @@ void MasterThread::run()
int currentWaitTimeout = waitTimeout;
QString currentRequest = request;
mutex.unlock();
-
+ //! [5] //! [6]
SerialPort serial;
while (!quit) {
-
+ //![6] //! [7]
if (currentPortNameChanged) {
serial.close();
serial.setPort(currentPortName);
@@ -133,12 +140,12 @@ void MasterThread::run()
return;
}
}
-
+ //! [7] //! [8]
// write request
QByteArray requestData = currentRequest.toLocal8Bit();
serial.write(requestData);
if (serial.waitForBytesWritten(waitTimeout)) {
-
+ //! [8] //! [10]
// read response
if (serial.waitForReadyRead(currentWaitTimeout)) {
QByteArray responseData = serial.readAll();
@@ -146,16 +153,19 @@ void MasterThread::run()
responseData += serial.readAll();
QString response(responseData);
+ //! [12]
emit this->response(response);
+ //! [10] //! [11] //! [12]
} else {
emit timeout(tr("Wait read response timeout %1")
.arg(QTime::currentTime().toString()));
}
+ //! [9] //! [11]
} else {
emit timeout(tr("Wait write request timeout %1")
.arg(QTime::currentTime().toString()));
}
-
+ //! [9] //! [13]
mutex.lock();
cond.wait(&mutex);
if (currentPortName != portName) {
@@ -168,4 +178,5 @@ void MasterThread::run()
currentRequest = request;
mutex.unlock();
}
+ //! [13]
}
diff --git a/examples/blockingmaster/masterthread.h b/examples/blockingmaster/masterthread.h
index d4f51f23..f4f0d837 100644
--- a/examples/blockingmaster/masterthread.h
+++ b/examples/blockingmaster/masterthread.h
@@ -46,6 +46,7 @@
#include <QMutex>
#include <QWaitCondition>
+//! [0]
class MasterThread : public QThread
{
Q_OBJECT
@@ -70,5 +71,6 @@ private:
QWaitCondition cond;
bool quit;
};
+//! [0]
#endif // MASTERTHREAD_H