QGIS API Documentation 3.99.0-Master (8e76e220402)
Loading...
Searching...
No Matches
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
19#include "qgslogger.h"
20#include "qgssensorregistry.h"
21
22#include <QDomElement>
23#include <QString>
24
25#include "moc_qgsiodevicesensor.cpp"
26
27using namespace Qt::StringLiterals;
28
29#if defined( Q_OS_ANDROID ) || defined( Q_OS_LINUX )
30#include <sys/socket.h>
31#endif
32
36
37void QgsIODeviceSensor::initIODevice( QIODevice *device )
38{
39 mIODevice.reset( device );
40
41 if ( mIODevice )
42 {
43 connect( mIODevice.get(), &QIODevice::readyRead, this, &QgsIODeviceSensor::parseData );
44 }
45}
46
48{
49 return mIODevice.get();
50}
51
53{
55 data.lastValue = mIODevice->readAll();
56 data.lastTimestamp = QDateTime::currentDateTime();
57 setData( data );
58}
59
60//--------------
61
63 : QgsIODeviceSensor( parent )
64 , mTcpSocket( new QTcpSocket() )
65{
66 connect( mTcpSocket, &QAbstractSocket::stateChanged, this, &QgsTcpSocketSensor::socketStateChanged );
67 connect( mTcpSocket, qOverload<QAbstractSocket::SocketError>( &QAbstractSocket::errorOccurred ), this, &QgsTcpSocketSensor::handleError );
68
69 initIODevice( mTcpSocket );
70}
71
73{
74 return new QgsTcpSocketSensor( parent );
75}
76
78{
79 return "tcp_socket"_L1;
80}
81
83{
84 return mHostName;
85}
86
88{
89 if ( mHostName == hostName )
90 return;
91
92 mHostName = hostName;
93}
94
96{
97 return mPort;
98}
99
101{
102 if ( mPort == port || port < 1 )
103 return;
104
105 mPort = port;
106}
107
109{
110 if ( mHostName.isEmpty() || mPort == 0 )
111 {
113 return;
114 }
115
116 mTcpSocket->connectToHost( mHostName, mPort, QTcpSocket::ReadOnly );
117}
118
120{
121 mTcpSocket->close();
122}
123
124void QgsTcpSocketSensor::handleError( QAbstractSocket::SocketError error )
125{
126 switch ( error )
127 {
128 case QAbstractSocket::HostNotFoundError:
129 mErrorString = tr( "Could not find the remote host" );
130 break;
131 case QAbstractSocket::NetworkError:
132 mErrorString = tr( "Attempt to read or write from socket returned an error" );
133 break;
134 case QAbstractSocket::ConnectionRefusedError:
135 mErrorString = tr( "The connection was refused by the remote host" );
136 break;
137 default:
138 mErrorString = tr( "%1" ).arg( QMetaEnum::fromType<QAbstractSocket::SocketError>().valueToKey( error ) );
139 break;
140 }
141
143}
144
145void QgsTcpSocketSensor::socketStateChanged( const QAbstractSocket::SocketState socketState )
146{
147 switch ( socketState )
148 {
149 case QAbstractSocket::ConnectedState:
150 {
152 break;
153 }
154 case QAbstractSocket::UnconnectedState:
155 {
157 break;
158 }
159 default:
160 break;
161 }
162}
163
164bool QgsTcpSocketSensor::writePropertiesToElement( QDomElement &element, QDomDocument & ) const
165{
166 element.setAttribute( u"hostName"_s, mHostName );
167 element.setAttribute( u"port"_s, QString::number( mPort ) );
168
169 return true;
170}
171
172bool QgsTcpSocketSensor::readPropertiesFromElement( const QDomElement &element, const QDomDocument & )
173{
174 mHostName = element.attribute( u"hostName"_s );
175 mPort = element.attribute( u"port"_s ).toInt();
176
177 return true;
178}
179
180//--------------
181
183 : QgsIODeviceSensor( parent )
184 , mUdpSocket( std::make_unique<QUdpSocket>() )
185 , mBuffer( new QBuffer() )
186{
187#if defined( Q_OS_ANDROID ) || defined( Q_OS_LINUX )
188 int sockfd = socket( AF_INET, SOCK_DGRAM, 0 );
189 int optval = 1;
190 setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR,
191 ( void * ) &optval, sizeof( optval ) );
192 mUdpSocket->setSocketDescriptor( sockfd, QUdpSocket::UnconnectedState );
193#endif
194
195 connect( mUdpSocket.get(), &QAbstractSocket::stateChanged, this, &QgsUdpSocketSensor::socketStateChanged );
196 connect( mUdpSocket.get(), &QUdpSocket::readyRead, this, [this]()
197 {
198 QByteArray datagram;
199 while ( mUdpSocket->hasPendingDatagrams() )
200 {
201 datagram.resize( int( mUdpSocket->pendingDatagramSize() ) );
202 mUdpSocket->readDatagram( datagram.data(), datagram.size() );
203
204 mBuffer->buffer().clear();
205 mBuffer->seek( 0 );
206 mBuffer->write( datagram );
207 mBuffer->seek( 0 );
208 }
209 } );
210
211 connect( mUdpSocket.get(), qOverload<QAbstractSocket::SocketError>( &QAbstractSocket::errorOccurred ), this, &QgsUdpSocketSensor::handleError );
212
213 initIODevice( mBuffer );
214}
215
217{
218 return new QgsUdpSocketSensor( parent );
219}
220
222{
223 return "udp_socket"_L1;
224}
225
227{
228 return mHostName;
229}
230
232{
233 if ( mHostName == hostName )
234 return;
235
236 mHostName = hostName;
237}
238
240{
241 return mPort;
242}
243
245{
246 if ( mPort == port || port < 1 )
247 return;
248
249 mPort = port;
250}
251
253{
254#ifdef QT_NO_NETWORKINTERFACE
255 Q_UNUSED( mHostName )
256 Q_UNUSED( mPort )
258 QgsDebugError( u"Qt is built without network interface support, cannot use UDP sockets."_s );
259#else
260 if ( mHostName.isEmpty() || mPort == 0 )
261 {
263 return;
264 }
265
266 mBuffer->open( QIODevice::ReadWrite );
267 mUdpSocket->bind( QHostAddress( mHostName ), mPort, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint );
268 mUdpSocket->joinMulticastGroup( QHostAddress( mHostName ) );
269#endif
270}
271
273{
274 mUdpSocket->close();
275 mBuffer->close();
276}
277
278void QgsUdpSocketSensor::handleError( QAbstractSocket::SocketError error )
279{
280 switch ( error )
281 {
282 case QAbstractSocket::HostNotFoundError:
283 mErrorString = tr( "Could not find the remote host" );
284 break;
285 case QAbstractSocket::NetworkError:
286 mErrorString = tr( "Attempt to read or write from socket returned an error" );
287 break;
288 case QAbstractSocket::ConnectionRefusedError:
289 mErrorString = tr( "The connection was refused by the remote host" );
290 break;
291 default:
292 mErrorString = tr( "%1" ).arg( QMetaEnum::fromType<QAbstractSocket::SocketError>().valueToKey( error ) );
293 break;
294 }
295
297}
298
299void QgsUdpSocketSensor::socketStateChanged( const QAbstractSocket::SocketState socketState )
300{
301 switch ( socketState )
302 {
303 case QAbstractSocket::ConnectedState:
304 case QAbstractSocket::BoundState:
305 {
307 break;
308 }
309 case QAbstractSocket::UnconnectedState:
310 {
312 break;
313 }
314 default:
315 break;
316 }
317}
318
319bool QgsUdpSocketSensor::writePropertiesToElement( QDomElement &element, QDomDocument & ) const
320{
321 element.setAttribute( u"hostName"_s, mHostName );
322 element.setAttribute( u"port"_s, QString::number( mPort ) );
323
324 return true;
325}
326
327bool QgsUdpSocketSensor::readPropertiesFromElement( const QDomElement &element, const QDomDocument & )
328{
329 mHostName = element.attribute( u"hostName"_s );
330 mPort = element.attribute( u"port"_s ).toInt();
331
332 return true;
333}
334
335//--------------
336
337#if defined( HAVE_QTSERIALPORT )
338QgsSerialPortSensor::QgsSerialPortSensor( QObject *parent )
339 : QgsIODeviceSensor( parent )
340 , mSerialPort( new QSerialPort() )
341{
342 connect( mSerialPort, qOverload<QSerialPort::SerialPortError>( &QSerialPort::errorOccurred ), this, &QgsSerialPortSensor::handleError );
343
344 initIODevice( mSerialPort );
345}
346
347QgsSerialPortSensor *QgsSerialPortSensor::create( QObject *parent )
348{
349 return new QgsSerialPortSensor( parent );
350}
351
352QString QgsSerialPortSensor::type() const
353{
354 return "serial_port"_L1;
355}
356
357QString QgsSerialPortSensor::portName() const
358{
359 return mPortName;
360}
361
362void QgsSerialPortSensor::setPortName( const QString &portName )
363{
364 if ( mPortName == portName )
365 return;
366
367 mPortName = portName;
368}
369
370QSerialPort::BaudRate QgsSerialPortSensor::baudRate() const
371{
372 return mBaudRate;
373}
374
375void QgsSerialPortSensor::setBaudRate( const QSerialPort::BaudRate &baudRate )
376{
377 if ( mBaudRate == baudRate )
378 return;
379
380 mBaudRate = baudRate;
381}
382
383QByteArray QgsSerialPortSensor::delimiter() const
384{
385 return mDelimiter;
386}
387
388void QgsSerialPortSensor::setDelimiter( const QByteArray &delimiter )
389{
390 if ( mDelimiter == delimiter )
391 return;
392
393 mDelimiter = delimiter;
394}
395
396
397void QgsSerialPortSensor::parseData()
398{
399 if ( !mDelimiter.isEmpty() )
400 {
401 if ( mFirstDelimiterHit )
402 {
403 mDataBuffer += mSerialPort->readAll();
404 const auto lastIndex = mDataBuffer.lastIndexOf( mDelimiter );
405 if ( lastIndex > -1 )
406 {
408 data.lastValue = mDataBuffer.mid( 0, lastIndex );
409 mDataBuffer = mDataBuffer.mid( lastIndex + mDelimiter.size() );
410 data.lastTimestamp = QDateTime::currentDateTime();
411 setData( data );
412 }
413 }
414 else
415 {
416 QByteArray data = mSerialPort->readAll();
417 const auto lastIndex = data.lastIndexOf( mDelimiter );
418 if ( lastIndex > -1 )
419 {
420 mFirstDelimiterHit = true;
421 mDataBuffer = data.mid( lastIndex + mDelimiter.size() );
422 }
423 }
424 }
425 else
426 {
428 data.lastValue = mSerialPort->readAll();
429 data.lastTimestamp = QDateTime::currentDateTime();
430 setData( data );
431 }
432}
433
434void QgsSerialPortSensor::handleConnect()
435{
436 mSerialPort->setPortName( mPortName );
437 mSerialPort->setBaudRate( mBaudRate );
438 mFirstDelimiterHit = false;
439
440 if ( mSerialPort->open( QIODevice::ReadOnly ) )
441 {
443 }
444 else
445 {
447 }
448}
449
450void QgsSerialPortSensor::handleDisconnect()
451{
452 mSerialPort->close();
453}
454
455void QgsSerialPortSensor::handleError( QSerialPort::SerialPortError error )
456{
457 if ( error == QSerialPort::NoError )
458 {
459 return;
460 }
461
462 switch ( error )
463 {
464 case QSerialPort::DeviceNotFoundError:
465 mErrorString = tr( "Could not find the serial port device" );
466 break;
467 case QSerialPort::ReadError:
468 mErrorString = tr( "Attempt to read from the serial port returned an error" );
469 break;
470 case QSerialPort::PermissionError:
471 mErrorString = tr( "The connection was refused due to not having enough permission" );
472 break;
473 default:
474 mErrorString = tr( "%1" ).arg( QMetaEnum::fromType<QSerialPort::SerialPortError>().valueToKey( error ) );
475 break;
476 }
477
478 emit errorOccurred( mErrorString );
479}
480
481bool QgsSerialPortSensor::writePropertiesToElement( QDomElement &element, QDomDocument & ) const
482{
483 element.setAttribute( u"portName"_s, mPortName );
484 element.setAttribute( u"baudRate"_s, static_cast<int>( mBaudRate ) );
485 element.setAttribute( u"delimiter"_s, QString( mDelimiter ) );
486 return true;
487}
488
489bool QgsSerialPortSensor::readPropertiesFromElement( const QDomElement &element, const QDomDocument & )
490{
491 mPortName = element.attribute( u"portName"_s );
492 mBaudRate = static_cast< QSerialPort::BaudRate >( element.attribute( u"baudRate"_s ).toInt() );
493 mDelimiter = element.attribute( u"delimiter"_s ).toLocal8Bit();
494 return true;
495}
496#endif
@ Connected
Device is successfully connected.
Definition qgis.h:1935
@ Disconnected
Device is disconnected.
Definition qgis.h:1933
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 for QIODevice-based sensors.
void initIODevice(QIODevice *device)
Initiates the I/O device.
QgsIODeviceSensor(QObject *parent=nullptr)
Constructor for a abstract QIODevice-based sensor, bound to the specified parent.
virtual void parseData()
Parses the data read from the device when available.
QIODevice * iODevice() const
Returns the I/O device.
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.
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.
#define QgsDebugError(str)
Definition qgslogger.h:59
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.