QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
qgsiodevicesensor.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsiodevicesensor.cpp
3 ---------------------------
4 begin : March 2023
5 copyright : (C) 2023 by Mathieu Pellerin
6 email : mathieu at opengis dot ch
7 ***************************************************************************/
8/***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17#include "qgsiodevicesensor.h"
18#include "qgssensorregistry.h"
19
20#include <QDomElement>
21
22#if defined( Q_OS_ANDROID ) || defined( Q_OS_LINUX )
23#include <sys/socket.h>
24#endif
25
27{
28}
29
30void QgsIODeviceSensor::initIODevice( QIODevice *device )
31{
32 mIODevice.reset( device );
33
34 if ( mIODevice )
35 {
36 connect( mIODevice.get(), &QIODevice::readyRead, this, &QgsIODeviceSensor::parseData );
37 }
38}
39
41{
42 return mIODevice.get();
43}
44
46{
48 data.lastValue = mIODevice->readAll();
49 data.lastTimestamp = QDateTime::currentDateTime();
50 setData( data );
51}
52
53//--------------
54
56 : QgsIODeviceSensor( parent )
57 , mTcpSocket( new QTcpSocket() )
58{
59 connect( mTcpSocket, &QAbstractSocket::stateChanged, this, &QgsTcpSocketSensor::socketStateChanged );
60
61#if QT_VERSION < QT_VERSION_CHECK( 5, 15, 0 )
62 connect( mTcpSocket, qOverload<QAbstractSocket::SocketError>( &QAbstractSocket::error ), this, &QgsTcpSocketSensor::handleError );
63#else
64 connect( mTcpSocket, qOverload<QAbstractSocket::SocketError>( &QAbstractSocket::errorOccurred ), this, &QgsTcpSocketSensor::handleError );
65#endif
66
67 initIODevice( mTcpSocket );
68}
69
71{
72 return new QgsTcpSocketSensor( parent );
73}
74
76{
77 return QLatin1String( "tcp_socket" );
78}
79
81{
82 return mHostName;
83}
84
85void QgsTcpSocketSensor::setHostName( const QString &hostName )
86{
87 if ( mHostName == hostName )
88 return;
89
90 mHostName = hostName;
91}
92
94{
95 return mPort;
96}
97
99{
100 if ( mPort == port || port < 1 )
101 return;
102
103 mPort = port;
104}
105
107{
108 if ( mHostName.isEmpty() || mPort == 0 )
109 {
111 return;
112 }
113
114 mTcpSocket->connectToHost( mHostName, mPort, QTcpSocket::ReadOnly );
115}
116
118{
119 mTcpSocket->close();
120}
121
122void QgsTcpSocketSensor::handleError( QAbstractSocket::SocketError error )
123{
124 switch ( error )
125 {
126 case QAbstractSocket::HostNotFoundError:
127 mErrorString = tr( "Could not find the remote host" );
128 break;
129 case QAbstractSocket::NetworkError:
130 mErrorString = tr( "Attempt to read or write from socket returned an error" );
131 break;
132 case QAbstractSocket::ConnectionRefusedError:
133 mErrorString = tr( "The connection was refused by the remote host" );
134 break;
135 default:
136 mErrorString = tr( "%1" ).arg( QMetaEnum::fromType<QAbstractSocket::SocketError>().valueToKey( error ) );
137 break;
138 }
139
141}
142
143void QgsTcpSocketSensor::socketStateChanged( const QAbstractSocket::SocketState socketState )
144{
145 switch ( socketState )
146 {
147 case QAbstractSocket::ConnectedState:
148 {
150 break;
151 }
152 case QAbstractSocket::UnconnectedState:
153 {
155 break;
156 }
157 default:
158 break;
159 }
160}
161
162bool QgsTcpSocketSensor::writePropertiesToElement( QDomElement &element, QDomDocument & ) const
163{
164 element.setAttribute( QStringLiteral( "hostName" ), mHostName );
165 element.setAttribute( QStringLiteral( "port" ), QString::number( mPort ) );
166
167 return true;
168}
169
170bool QgsTcpSocketSensor::readPropertiesFromElement( const QDomElement &element, const QDomDocument & )
171{
172 mHostName = element.attribute( QStringLiteral( "hostName" ) );
173 mPort = element.attribute( QStringLiteral( "port" ) ).toInt();
174
175 return true;
176}
177
178//--------------
179
181 : QgsIODeviceSensor( parent )
182 , mUdpSocket( std::make_unique<QUdpSocket>() )
183 , mBuffer( new QBuffer() )
184{
185#if defined( Q_OS_ANDROID ) || defined( Q_OS_LINUX )
186 int sockfd = socket( AF_INET, SOCK_DGRAM, 0 );
187 int optval = 1;
188 setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR,
189 ( void * ) &optval, sizeof( optval ) );
190 mUdpSocket->setSocketDescriptor( sockfd, QUdpSocket::UnconnectedState );
191#endif
192
193 connect( mUdpSocket.get(), &QAbstractSocket::stateChanged, this, &QgsUdpSocketSensor::socketStateChanged );
194 connect( mUdpSocket.get(), &QUdpSocket::readyRead, this, [ = ]()
195 {
196 QByteArray datagram;
197 while ( mUdpSocket->hasPendingDatagrams() )
198 {
199 datagram.resize( int( mUdpSocket->pendingDatagramSize() ) );
200 mUdpSocket->readDatagram( datagram.data(), datagram.size() );
201
202 mBuffer->buffer().clear();
203 mBuffer->seek( 0 );
204 mBuffer->write( datagram );
205 mBuffer->seek( 0 );
206 }
207 } );
208
209#if QT_VERSION < QT_VERSION_CHECK( 5, 15, 0 )
210 connect( mUdpSocket.get(), qOverload<QAbstractSocket::SocketError>( &QAbstractSocket::error ), this, &QgsUdpSocketSensor::handleError );
211#else
212 connect( mUdpSocket.get(), qOverload<QAbstractSocket::SocketError>( &QAbstractSocket::errorOccurred ), this, &QgsUdpSocketSensor::handleError );
213#endif
214
215 initIODevice( mBuffer );
216}
217
219{
220 return new QgsUdpSocketSensor( parent );
221}
222
224{
225 return QLatin1String( "udp_socket" );
226}
227
229{
230 return mHostName;
231}
232
233void QgsUdpSocketSensor::setHostName( const QString &hostName )
234{
235 if ( mHostName == hostName )
236 return;
237
238 mHostName = hostName;
239}
240
242{
243 return mPort;
244}
245
247{
248 if ( mPort == port || port < 1 )
249 return;
250
251 mPort = port;
252}
253
255{
256 if ( mHostName.isEmpty() || mPort == 0 )
257 {
259 return;
260 }
261
262 mBuffer->open( QIODevice::ReadWrite );
263 mUdpSocket->bind( QHostAddress( mHostName ), mPort, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint );
264 mUdpSocket->joinMulticastGroup( QHostAddress( mHostName ) );
265}
266
268{
269 mUdpSocket->close();
270 mBuffer->close();
271}
272
273void QgsUdpSocketSensor::handleError( QAbstractSocket::SocketError error )
274{
275 switch ( error )
276 {
277 case QAbstractSocket::HostNotFoundError:
278 mErrorString = tr( "Could not find the remote host" );
279 break;
280 case QAbstractSocket::NetworkError:
281 mErrorString = tr( "Attempt to read or write from socket returned an error" );
282 break;
283 case QAbstractSocket::ConnectionRefusedError:
284 mErrorString = tr( "The connection was refused by the remote host" );
285 break;
286 default:
287 mErrorString = tr( "%1" ).arg( QMetaEnum::fromType<QAbstractSocket::SocketError>().valueToKey( error ) );
288 break;
289 }
290
292}
293
294void QgsUdpSocketSensor::socketStateChanged( const QAbstractSocket::SocketState socketState )
295{
296 switch ( socketState )
297 {
298 case QAbstractSocket::ConnectedState:
299 case QAbstractSocket::BoundState:
300 {
302 break;
303 }
304 case QAbstractSocket::UnconnectedState:
305 {
307 break;
308 }
309 default:
310 break;
311 }
312}
313
314bool QgsUdpSocketSensor::writePropertiesToElement( QDomElement &element, QDomDocument & ) const
315{
316 element.setAttribute( QStringLiteral( "hostName" ), mHostName );
317 element.setAttribute( QStringLiteral( "port" ), QString::number( mPort ) );
318
319 return true;
320}
321
322bool QgsUdpSocketSensor::readPropertiesFromElement( const QDomElement &element, const QDomDocument & )
323{
324 mHostName = element.attribute( QStringLiteral( "hostName" ) );
325 mPort = element.attribute( QStringLiteral( "port" ) ).toInt();
326
327 return true;
328}
329
330//--------------
331
332#if defined( HAVE_QTSERIALPORT )
333QgsSerialPortSensor::QgsSerialPortSensor( QObject *parent )
334 : QgsIODeviceSensor( parent )
335 , mSerialPort( new QSerialPort() )
336{
337 connect( mSerialPort, qOverload<QSerialPort::SerialPortError>( &QSerialPort::errorOccurred ), this, &QgsSerialPortSensor::handleError );
338
339 initIODevice( mSerialPort );
340}
341
342QgsSerialPortSensor *QgsSerialPortSensor::create( QObject *parent )
343{
344 return new QgsSerialPortSensor( parent );
345}
346
347QString QgsSerialPortSensor::type() const
348{
349 return QLatin1String( "serial_port" );
350}
351
352QString QgsSerialPortSensor::portName() const
353{
354 return mPortName;
355}
356
357void QgsSerialPortSensor::setPortName( const QString &portName )
358{
359 if ( mPortName == portName )
360 return;
361
362 mPortName = portName;
363}
364
365void QgsSerialPortSensor::handleConnect()
366{
367 mSerialPort->setPortName( mPortName );
368 mSerialPort->setBaudRate( QSerialPort::Baud9600 );
369 if ( mSerialPort->open( QIODevice::ReadOnly ) )
370 {
372 }
373 else
374 {
376 }
377}
378
379void QgsSerialPortSensor::handleDisconnect()
380{
381 mSerialPort->close();
382}
383
384void QgsSerialPortSensor::handleError( QSerialPort::SerialPortError error )
385{
386 if ( error == QSerialPort::NoError )
387 {
388 return;
389 }
390
391 switch ( error )
392 {
393 case QSerialPort::DeviceNotFoundError:
394 mErrorString = tr( "Could not find the serial port device" );
395 break;
396 case QSerialPort::ReadError:
397 mErrorString = tr( "Attempt to read from the serial port returned an error" );
398 break;
399 case QSerialPort::PermissionError:
400 mErrorString = tr( "The connection was refused due to not having enough permission" );
401 break;
402 default:
403 mErrorString = tr( "%1" ).arg( QMetaEnum::fromType<QSerialPort::SerialPortError>().valueToKey( error ) );
404 break;
405 }
406
407 emit errorOccurred( mErrorString );
408}
409
410bool QgsSerialPortSensor::writePropertiesToElement( QDomElement &element, QDomDocument & ) const
411{
412 element.setAttribute( QStringLiteral( "portName" ), mPortName );
413
414 return true;
415}
416
417bool QgsSerialPortSensor::readPropertiesFromElement( const QDomElement &element, const QDomDocument & )
418{
419 mPortName = element.attribute( QStringLiteral( "portName" ) );
420
421 return true;
422}
423#endif
@ Connected
Device is successfully connected.
@ Disconnected
Device is disconnected.
QgsAbstractSensor::SensorData data() const
Returns the latest captured data from the sensor.
void setStatus(Qgis::DeviceConnectionStatus status)
Sets the current sensor status.
void errorOccurred(const QString &errorString)
Emitted when an error has occurred. The errorString describes the error.
void setData(const QgsAbstractSensor::SensorData &data)
Sets the latest captured data from the sensor.
An abstract class QIODevice-based sensor classes.
void initIODevice(QIODevice *device)
Initiates the I/O device.
virtual void parseData()
Parses the data read from the device when available.
QIODevice * iODevice() const
Returns the I/O device.
~QgsIODeviceSensor() override
A TCP socket sensor class.
QgsTcpSocketSensor(QObject *parent=nullptr)
Constructor for a TCP socket sensor, bound to the specified parent.
void setHostName(const QString &hostName)
Sets the host name the socket connects to.
int port() const
Returns the port the socket connects to.
void handleConnect() override
Handles the connection to the sensor.
QString type() const override
Returns the sensor type.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document) const override
Write specific sensor type properties into a DOM element.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document) override
Restores specific sensor type properties from a DOM element.
void handleDisconnect() override
Handles the disconnection from the sensor.
static QgsTcpSocketSensor * create(QObject *parent)
Returns a new TCP socket sensor.
void setPort(int port)
Sets the port the socket connects to.
QString hostName() const
Returns the host name the socket connects to.
A UDP socket sensor class.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document) override
Restores specific sensor type properties from a DOM element.
static QgsUdpSocketSensor * create(QObject *parent)
Returns a new UDP socket sensor.
QString type() const override
Returns the sensor type.
void handleDisconnect() override
Handles the disconnection from the sensor.
QgsUdpSocketSensor(QObject *parent=nullptr)
Constructor for a UDP socket sensor, bound to the specified parent.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document) const override
Write specific sensor type properties into a DOM element.
void setHostName(const QString &hostName)
Sets the host name the socket connects to.
int port() const
Returns the port the socket connects to.
void handleConnect() override
Handles the connection to the sensor.
void setPort(int port)
Sets the port the socket connects to.
QString hostName() const
Returns the host name the socket connects to.
Contains details of a sensor data capture.
QVariant lastValue
Last captured sensor value stored as a QVariant.
QDateTime lastTimestamp
Timestamp of last captured sensor value.