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