1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtSerialBus module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36 
37 #include "qmodbustcpserver.h"
38 #include "qmodbustcpserver_p.h"
39 
40 #include <QtCore/qurl.h>
41 
42 QT_BEGIN_NAMESPACE
43 
44 /*!
45     \class QModbusTcpServer
46     \inmodule QtSerialBus
47     \since 5.8
48 
49     \brief The QModbusTcpServer class represents a Modbus server that uses a
50     TCP server for its communication with the Modbus client.
51 
52     Communication via Modbus requires the interaction between a single Modbus
53     client instance and single Modbus server. This class provides the Modbus
54     server implementation via a TCP server.
55 
56     Modbus TCP networks can have multiple servers. Servers are read/written by
57     a client device represented by \l QModbusTcpClient.
58 */
59 
60 /*!
61     Constructs a QModbusTcpServer with the specified \a parent. The
62     \l serverAddress preset is \c 255.
63 */
QModbusTcpServer(QObject * parent)64 QModbusTcpServer::QModbusTcpServer(QObject *parent)
65     : QModbusServer(*new QModbusTcpServerPrivate, parent)
66 {
67     Q_D(QModbusTcpServer);
68     d->setupTcpServer();
69     setServerAddress(0xff);
70 }
71 
72 /*!
73     Destroys the QModbusTcpServer instance.
74 */
~QModbusTcpServer()75 QModbusTcpServer::~QModbusTcpServer()
76 {
77     close();
78 }
79 
80 /*!
81     \internal
82 */
QModbusTcpServer(QModbusTcpServerPrivate & dd,QObject * parent)83 QModbusTcpServer::QModbusTcpServer(QModbusTcpServerPrivate &dd, QObject *parent)
84     : QModbusServer(dd, parent)
85 {
86     Q_D(QModbusTcpServer);
87     d->setupTcpServer();
88 }
89 
90 /*!
91     \reimp
92 */
open()93 bool QModbusTcpServer::open()
94 {
95     if (state() == QModbusDevice::ConnectedState)
96         return true;
97 
98     Q_D(QModbusTcpServer);
99     if (d->m_tcpServer->isListening())
100         return false;
101 
102     const QUrl url = QUrl::fromUserInput(d->m_networkAddress + QStringLiteral(":")
103         + QString::number(d->m_networkPort));
104 
105     if (!url.isValid()) {
106         setError(tr("Invalid connection settings for TCP communication specified."),
107             QModbusDevice::ConnectionError);
108         qCWarning(QT_MODBUS) << "(TCP server) Invalid host:" << url.host() << "or port:"
109             << url.port();
110         return false;
111     }
112 
113     if (d->m_tcpServer->listen(QHostAddress(url.host()), quint16(url.port())))
114         setState(QModbusDevice::ConnectedState);
115     else
116         setError(d->m_tcpServer->errorString(), QModbusDevice::ConnectionError);
117 
118     return state() == QModbusDevice::ConnectedState;
119 }
120 
121 /*!
122     \reimp
123 */
close()124 void QModbusTcpServer::close()
125 {
126     if (state() == QModbusDevice::UnconnectedState)
127         return;
128 
129     Q_D(QModbusTcpServer);
130 
131     if (d->m_tcpServer->isListening())
132         d->m_tcpServer->close();
133 
134     for (auto socket : qAsConst(d->connections))
135         socket->disconnectFromHost();
136 
137     setState(QModbusDevice::UnconnectedState);
138 }
139 
140 /*!
141     \reimp
142 
143     Processes the Modbus client request specified by \a request and returns a
144     Modbus response.
145 
146     The following Modbus function codes are filtered out as they are serial
147     line only according to the Modbus Application Protocol Specification 1.1b:
148     \list
149         \li \l QModbusRequest::ReadExceptionStatus
150         \li \l QModbusRequest::Diagnostics
151         \li \l QModbusRequest::GetCommEventCounter
152         \li \l QModbusRequest::GetCommEventLog
153         \li \l QModbusRequest::ReportServerId
154     \endlist
155     A request to the TCP server will be answered with a Modbus exception
156     response with the exception code QModbusExceptionResponse::IllegalFunction.
157 */
processRequest(const QModbusPdu & request)158 QModbusResponse QModbusTcpServer::processRequest(const QModbusPdu &request)
159 {
160     switch (request.functionCode()) {
161     case QModbusRequest::ReadExceptionStatus:
162     case QModbusRequest::Diagnostics:
163     case QModbusRequest::GetCommEventCounter:
164     case QModbusRequest::GetCommEventLog:
165     case QModbusRequest::ReportServerId:
166         return QModbusExceptionResponse(request.functionCode(),
167             QModbusExceptionResponse::IllegalFunction);
168     default:
169         break;
170     }
171     return QModbusServer::processRequest(request);
172 }
173 
174 /*!
175   Installs  an \a observer that can be used to obtain notifications when a
176   new TCP client connects to this server instance. In addition, the \a observer
177   can be used to reject the incoming TCP connection.
178 
179   QModbusTcpServer takes ownership of the given \a observer. Any previously set
180   observer will be deleted. The observer can be uninstalled by calling this
181   function with \c nullptr as parameter.
182 
183   \sa QModbusTcpConnectionObserver
184   \since 5.13
185 */
installConnectionObserver(QModbusTcpConnectionObserver * observer)186 void QModbusTcpServer::installConnectionObserver(QModbusTcpConnectionObserver *observer)
187 {
188     Q_D(QModbusTcpServer);
189 
190     d->m_observer.reset(observer);
191 }
192 
193 /*!
194     \class QModbusTcpConnectionObserver
195     \inmodule QtSerialBus
196     \since 5.13
197 
198     \brief The QModbusTcpConnectionObserver class represents the interface for
199     objects that can be passed to \l QModbusTcpServer::installConnectionObserver.
200 
201     The interface must be implemented by the developer to be able to monitor
202     every incoming TCP connection from another Modbus client.
203 
204     \sa QModbusTcpServer::installConnectionObserver
205 */
206 
~QModbusTcpConnectionObserver()207 QModbusTcpConnectionObserver::~QModbusTcpConnectionObserver()
208 {
209 }
210 
211 /*!
212   \fn bool QModbusTcpConnectionObserver::acceptNewConnection(QTcpSocket *newClient)
213 
214   This function is a callback for every incoming TCP connection. The user should
215   provide \a newClient to receive a notification when a new client connection
216   is established and to determine whether the connection is to be accepted.
217 
218   The function should return \c true if the connection is to be accepted. Otherwise,
219   the socket is closed/rejected.
220 */
221 
222 /*!
223   \fn void QModbusTcpServer::modbusClientDisconnected(QTcpSocket *modbusClient)
224 
225   This signal is emitted when a current TCP based \a modbusClient disconnects
226   from this Modbus TCP server. Note that there might be several TCP clients
227   connected at the same time.
228 
229   Notifications on incoming new connections can be received by installing a
230   QModbusTcpConnectionObserver via \l installConnectionObserver().
231 
232   \sa installConnectionObserver
233   \since 5.13
234 */
235 
236 QT_END_NAMESPACE
237