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

source code of qtserialbus/src/serialbus/qmodbustcpserver.cpp