blob: d3f7e4eb0b006b7668eb58f241709b544f6b5808 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2011 - 2012 Denis Shienkov <denis.shienkov@gmail.com>
** Copyright (C) 2012 - 2013 Laszlo Papp <lpapp@kde.org>
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** 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. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example blockingmaster
\title Blocking Master Example
\ingroup qtserialport-examples
\brief Shows how to use the synchronous API of QSerialPort in a worker thread.
\e{Blocking Master} shows how to create an application for a serial
interface using the synchronous API of QSerialPort in a worker thread.
\image blockingmaster-example.png
QSerialPort supports two programming alternatives:
\list
\li \e{The asynchronous (non-blocking) alternative.} Operations are scheduled
and performed when the control returns to the Qt event loop. The QSerialPort
class emits a signal when the operation is finished. For example, the
\l{QIODevice::write()}{write()} method returns immediately. When the data is
sent to the serial port, the QSerialPort class emits the
\l{QIODevice::bytesWritten()}{bytesWritten()} signal.
\li \e{The synchronous (blocking) alternative.} In headless and multithreaded
applications, the wait method can be called (in this case,
\l{QSerialPort::waitForReadyRead()}{waitForReadyRead()}) to suspend the
calling thread until the operation has completed.
\endlist
In this example, the synchronous alternative is demonstrated. The
\l{terminal}{Terminal} example illustrates the asynchronous alternative.
The purpose of this example is to demonstrate how to simplify your serial
programming code without losing the responsiveness of the user interface.
The blocking serial programming API often leads to simpler code, but it
should only be used in non-GUI threads to keep the user interface
responsive.
This application is the master which demonstrates the work paired with the slave
application \l{blockingslave}{Blocking Slave Example}.
The master application initiates the transfer request via the serial port to
the slave application and waits for response.
\snippet blockingmaster/masterthread.h 0
MasterThread is a QThread subclass that provides API for scheduling
requests to the slave. This class provides signals for responding and
reporting errors. The transaction() method can be called to start up the
new master transaction with the desired request. The result is provided by
the response() signal. In case of any issues, the error() or timeout()
signal is emitted.
Note, the transaction() method is called in the main thread, but the
request is provided in the MasterThread thread. The MasterThread
data members are read and written concurrently in different threads, thus
the QMutex class is used to synchronize the access.
\snippet blockingmaster/masterthread.cpp 2
The transaction() method stores the serial port name, timeout and request
data. The mutex can be locked with QMutexLocker to protect this data. The
thread can be started then, unless it is already running. The
\l{QWaitCondition::wakeOne()}{wakeOne()} method is discussed later.
\snippet blockingmaster/masterthread.cpp 4
\snippet blockingmaster/masterthread.cpp 5
In the run() function, the first is to lock the QMutex object, then fetch the
serial port name, timeout and request data by using the member data. Having
that done, the QMutex lock is released.
Under no circumstance should the \c transaction() method be called
simultaneously with a process fetching the data. Note, while the QString
class is reentrant, it is not thread-safe. Thereby, it is not recommended to
read the serial port name in a request thread, and timeout or request data
in another thread. The MasterThread class can only handle one request at a
time.
The QSerialPort object is constructed on stack in the run() method before
entering the loop:
\snippet blockingmaster/masterthread.cpp 6
This makes it possible to create an object while running the loop. It also
means that all the object methods are executed in the scope of the run()
method.
It is checked inside the loop whether or not the serial port name of the
current transaction has changed. If this has changed, the serial port is
reopened and then reconfigured.
\snippet blockingmaster/masterthread.cpp 7
The loop will continue to request data, write to the serial port and wait
until all data is transferred.
\snippet blockingmaster/masterthread.cpp 8
\warning As for the blocking transfer, the
\l{QSerialPort::waitForBytesWritten()}{waitForBytesWritten()} method should be
used after each \l{QIODevice::write()}{write} method call. This will process all
the I/O routines instead of the Qt event loop.
The timeout() signal is emitted if a timeout error occurs when transferring
data.
\snippet blockingmaster/masterthread.cpp 9
There is a waiting period for response after a successful request, and then
it is read again.
\snippet blockingmaster/masterthread.cpp 10
\warning As for the blocking alternative, the
\l{QSerialPort::waitForReadyRead()}{waitForReadyRead()} method should be
used before each read() call. This will processes all the I/O routines
instead of the Qt event loop.
The timeout() signal is emitted if a timeout error occurs when receiving data.
\snippet blockingmaster/masterthread.cpp 11
When a transaction has been completed successfully, the response() signal contains
the data received from the slave application:
\snippet blockingmaster/masterthread.cpp 12
Afterwards, the thread goes to sleep until the next transaction appears.
The thread reads the new data after waking up by using the members and
runs the loop from the beginning.
\snippet blockingmaster/masterthread.cpp 13
\sa {Terminal Example}, {Blocking Slave Example}
\include examples-run.qdocinc
*/