summaryrefslogtreecommitdiffstats
path: root/examples/serialport/doc/blockingsender.qdoc
blob: 2702e184c23b7fd523b68f4db57a81c0846cc33a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// 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.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \example blockingsender
    \title Blocking Sender Example
    \ingroup qtserialport-examples
    \brief Shows how to use the synchronous API of QSerialPort in a worker thread.

    \e{Blocking Sender} shows how to create an application for a serial
    interface using the synchronous API of QSerialPort in a worker thread.

    \image blockingsender-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 sender which demonstrates the work paired with the receiver
    application \l{blockingreceiver}{Blocking Receiver Example}.

    The sender application initiates the transfer request via the serial port to
    the receiver application and waits for response.

    \snippet blockingsender/senderthread.h 0

    SenderThread is a QThread subclass that provides API for scheduling
    requests to the receiver. This class provides signals for responding and
    reporting errors. The transaction() method can be called to start up the
    new sender 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 SenderThread thread. The SenderThread
    data members are read and written concurrently in different threads, thus
    the QMutex class is used to synchronize the access.

    \snippet blockingsender/senderthread.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 blockingsender/senderthread.cpp 4
    \snippet blockingsender/senderthread.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 SenderThread 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 blockingsender/senderthread.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 blockingsender/senderthread.cpp 7

    The loop will continue to request data, write to the serial port and wait
    until all data is transferred.

    \snippet blockingsender/senderthread.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 blockingsender/senderthread.cpp 9

    There is a waiting period for response after a successful request, and then
    it is read again.

    \snippet blockingsender/senderthread.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 blockingsender/senderthread.cpp 11

    When a transaction has been completed successfully, the response() signal contains
    the data received from the receiver application:

    \snippet blockingsender/senderthread.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 blockingsender/senderthread.cpp 13

    \sa {Terminal Example}, {Blocking Receiver Example}

    \include examples-run.qdocinc
*/