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