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