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