summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2021-02-09 11:44:14 +0100
committerIvan Solovev <ivan.solovev@qt.io>2021-02-12 10:47:07 +0100
commitd8095d6875056f0a5926801adc57bbfe00d9d24f (patch)
treedf831928a7f2e319cf7cf3ba788f4727a3fbc1a2
parent4ca9fd760f1511e00301a8f13c10108fe83823e7 (diff)
downloadqtlocation-d8095d6875056f0a5926801adc57bbfe00d9d24f.tar.gz
QtPositioning: improve NMEA plugin
- Rename serialnmea to nmea - Add support for QTcpSocket - Add support for reading data from QFile [ChangeLog][QtPositioning] SerialNmea plugin is renamed to Nmea. It's now capable of working with QTcpSocket and QFile. Task-number: QTBUG-90491 Change-Id: I9f591785c27157d7decf4e61aa6edfbeaffcd6f0 Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
-rw-r--r--src/plugins/position/CMakeLists.txt4
-rw-r--r--src/plugins/position/nmea/CMakeLists.txt (renamed from src/plugins/position/serialnmea/CMakeLists.txt)11
-rw-r--r--src/plugins/position/nmea/nmea.pro16
-rw-r--r--src/plugins/position/nmea/plugin.json (renamed from src/plugins/position/serialnmea/plugin.json)4
-rw-r--r--src/plugins/position/nmea/qgeopositioninfosourcefactory_nmea.cpp388
-rw-r--r--src/plugins/position/nmea/qgeopositioninfosourcefactory_nmea.h (renamed from src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.h)10
-rw-r--r--src/plugins/position/nmea/qiopipe.cpp (renamed from src/plugins/position/serialnmea/qiopipe.cpp)0
-rw-r--r--src/plugins/position/nmea/qiopipe_p.h (renamed from src/plugins/position/serialnmea/qiopipe_p.h)3
-rw-r--r--src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp254
-rw-r--r--src/plugins/position/serialnmea/serialnmea.pro16
-rw-r--r--src/positioning/doc/src/plugins/nmea.qdoc154
-rw-r--r--src/positioning/doc/src/plugins/serialnmea.qdoc78
-rw-r--r--src/positioning/doc/src/qtpositioning-plugins.qdoc13
-rw-r--r--src/positioning/qnmeapositioninfosource.h2
14 files changed, 585 insertions, 368 deletions
diff --git a/src/plugins/position/CMakeLists.txt b/src/plugins/position/CMakeLists.txt
index eeeecab3..db2dfa06 100644
--- a/src/plugins/position/CMakeLists.txt
+++ b/src/plugins/position/CMakeLists.txt
@@ -17,6 +17,6 @@ endif()
if(ANDROID)
add_subdirectory(android)
endif()
-if(TARGET Qt::SerialPort)
- add_subdirectory(serialnmea)
+if(TARGET Qt::SerialPort AND TARGET Qt::Network)
+ add_subdirectory(nmea)
endif()
diff --git a/src/plugins/position/serialnmea/CMakeLists.txt b/src/plugins/position/nmea/CMakeLists.txt
index 023c9165..fd4d0d48 100644
--- a/src/plugins/position/serialnmea/CMakeLists.txt
+++ b/src/plugins/position/nmea/CMakeLists.txt
@@ -1,20 +1,21 @@
-# Generated from serialnmea.pro.
+# Generated from nmea.pro.
#####################################################################
-## QGeoPositionInfoSourceFactorySerialNmea Plugin:
+## QGeoPositionInfoSourceFactoryNmea Plugin:
#####################################################################
-qt_internal_add_plugin(QGeoPositionInfoSourceFactorySerialNmea
- OUTPUT_NAME qtposition_serialnmea
+qt_internal_add_plugin(QGeoPositionInfoSourceFactoryNmea
+ OUTPUT_NAME qtposition_nmea
TYPE position
SOURCES
- qgeopositioninfosourcefactory_serialnmea.cpp qgeopositioninfosourcefactory_serialnmea.h
+ qgeopositioninfosourcefactory_nmea.cpp qgeopositioninfosourcefactory_nmea.h
qiopipe.cpp qiopipe_p.h
LIBRARIES
Qt::CorePrivate
PUBLIC_LIBRARIES
Qt::Positioning
Qt::SerialPort
+ Qt::Network
)
#### Keys ignored in scope 1:.:.:serialnmea.pro:<TRUE>:
diff --git a/src/plugins/position/nmea/nmea.pro b/src/plugins/position/nmea/nmea.pro
new file mode 100644
index 00000000..f52d8812
--- /dev/null
+++ b/src/plugins/position/nmea/nmea.pro
@@ -0,0 +1,16 @@
+TARGET = qtposition_nmea
+
+QT = core-private positioning-private serialport
+
+HEADERS += \
+ qgeopositioninfosourcefactory_nmea.h qiopipe_p.h
+
+SOURCES += \
+ qgeopositioninfosourcefactory_nmea.cpp qiopipe.cpp
+
+OTHER_FILES += \
+ plugin.json
+
+PLUGIN_TYPE = position
+PLUGIN_CLASS_NAME = QGeoPositionInfoSourceFactoryNmea
+load(qt_plugin)
diff --git a/src/plugins/position/serialnmea/plugin.json b/src/plugins/position/nmea/plugin.json
index 826836c3..15d22f1f 100644
--- a/src/plugins/position/serialnmea/plugin.json
+++ b/src/plugins/position/nmea/plugin.json
@@ -1,6 +1,6 @@
{
- "Keys": ["serialnmea"],
- "Provider": "serialnmea",
+ "Keys": ["nmea"],
+ "Provider": "nmea",
"Position": true,
"Satellite": false,
"Monitor" : false,
diff --git a/src/plugins/position/nmea/qgeopositioninfosourcefactory_nmea.cpp b/src/plugins/position/nmea/qgeopositioninfosourcefactory_nmea.cpp
new file mode 100644
index 00000000..1b17cade
--- /dev/null
+++ b/src/plugins/position/nmea/qgeopositioninfosourcefactory_nmea.cpp
@@ -0,0 +1,388 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtPositioning module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgeopositioninfosourcefactory_nmea.h"
+#include <QtPositioning/QNmeaPositionInfoSource>
+#include <QtPositioning/QNmeaSatelliteInfoSource>
+#include <QtSerialPort/QSerialPort>
+#include <QtSerialPort/QSerialPortInfo>
+#include <QtNetwork/QTcpSocket>
+#include <QLoggingCategory>
+#include <QSet>
+#include <QUrl>
+#include <QFile>
+#include <QSharedPointer>
+#include "qiopipe_p.h"
+
+Q_LOGGING_CATEGORY(lcNmea, "qt.positioning.nmea")
+
+QT_BEGIN_NAMESPACE
+
+static const auto sourceParameterName = QStringLiteral("nmea.source");
+
+// This class is used only for SerialPort devices, because we can't open the
+// same serial port twice.
+// In case of files and sockets it's easier to explicitly create a QIODevice for
+// each new instance of Nmea*InfoSource.
+// Also QFile can't be directly used with QIOPipe, because QFile is not a
+// sequential device.
+// TcpSocket could be used with QIOPipe, but it complicates error handling
+// dramatically, as we would need to somehow forward socket errors through
+// QIOPipes to the clients.
+class IODeviceContainer
+{
+public:
+ IODeviceContainer() {}
+ IODeviceContainer(IODeviceContainer const&) = delete;
+ void operator=(IODeviceContainer const&) = delete;
+
+ QSharedPointer<QIOPipe> serial(const QString &portName)
+ {
+ if (m_serialPorts.contains(portName)) {
+ m_serialPorts[portName].refs++;
+ QIOPipe *endPipe = new QIOPipe(m_serialPorts[portName].proxy);
+ m_serialPorts[portName].proxy->addChildPipe(endPipe);
+ return QSharedPointer<QIOPipe>(endPipe);
+ }
+ IODevice device;
+ QSerialPort *port = new QSerialPort(portName);
+ port->setBaudRate(4800);
+ qCDebug(lcNmea) << "Opening serial port" << portName;
+ if (!port->open(QIODevice::ReadOnly)) {
+ qWarning("nmea: Failed to open %s", qPrintable(portName));
+ delete port;
+ return {};
+ }
+ qCDebug(lcNmea) << "Opened successfully";
+ device.device = port;
+ device.refs = 1;
+ device.proxy = new QIOPipe(port, QIOPipe::ProxyPipe);
+ m_serialPorts[portName] = device;
+ QIOPipe *endPipe = new QIOPipe(device.proxy);
+ device.proxy->addChildPipe(endPipe);
+ return QSharedPointer<QIOPipe>(endPipe);
+ }
+
+ void releaseSerial(const QString &portName, QSharedPointer<QIOPipe> &pipe)
+ {
+ if (!m_serialPorts.contains(portName))
+ return;
+
+ pipe.clear(); // make sure to release the pipe returned by getSerial, or else, if there are still refs, data will be leaked through it
+ IODevice &device = m_serialPorts[portName];
+ if (device.refs > 1) {
+ device.refs--;
+ return;
+ }
+
+ IODevice taken = m_serialPorts.take(portName);
+ taken.device->deleteLater();
+ }
+
+private:
+
+ struct IODevice {
+ QIODevice *device = nullptr;
+ QIOPipe *proxy = nullptr; // adding client pipes as children of proxy
+ // allows to dynamically add clients to one device.
+ unsigned int refs = 1;
+ };
+
+ QMap<QString, IODevice> m_serialPorts;
+};
+
+Q_GLOBAL_STATIC(IODeviceContainer, deviceContainer)
+
+// We use a string prefix to distinguish between the different data sources.
+// "socket:" means that we use a socket connection
+// "serial:" means that we use a serial port connection
+// "file:///", "qrc:///" and just plain strings mean that we try to use local
+// file.
+// Note: if we do not specify anything, or specify "serial:" without specifying
+// the port name, then we will try to search for a well-known serial port
+// device.
+class NmeaSource : public QNmeaPositionInfoSource
+{
+ Q_OBJECT
+public:
+ NmeaSource(QObject *parent, const QVariantMap &parameters);
+ NmeaSource(QObject *parent, const QString &fileName);
+ ~NmeaSource() override;
+ bool isValid() const
+ {
+ return !m_dataSource.isNull() || !m_fileSource.isNull() || !m_socket.isNull();
+ }
+
+private slots:
+ void onSocketError(QAbstractSocket::SocketError error);
+
+private:
+ void parseSourceParameter(const QString &source);
+ void addSerialDevice(const QString &requestedPort);
+ void setFileName(const QString &fileName);
+ void connectSocket(const QString &source);
+
+ QSharedPointer<QIOPipe> m_dataSource;
+ QScopedPointer<QFile> m_fileSource;
+ QScopedPointer<QTcpSocket> m_socket;
+ QString m_sourceName;
+};
+
+NmeaSource::NmeaSource(QObject *parent, const QVariantMap &parameters)
+ : QNmeaPositionInfoSource(RealTimeMode, parent)
+{
+ const QString source = parameters.value(sourceParameterName).toString();
+ parseSourceParameter(source);
+}
+
+NmeaSource::NmeaSource(QObject *parent, const QString &fileName)
+ : QNmeaPositionInfoSource(SimulationMode, parent)
+{
+ setFileName(fileName);
+}
+
+NmeaSource::~NmeaSource()
+{
+ deviceContainer->releaseSerial(m_sourceName, m_dataSource);
+}
+
+void NmeaSource::onSocketError(QAbstractSocket::SocketError error)
+{
+ m_socket->close();
+
+ switch (error) {
+ case QAbstractSocket::UnknownSocketError:
+ setError(QGeoPositionInfoSource::UnknownSourceError);
+ break;
+ case QAbstractSocket::SocketAccessError:
+ setError(QGeoPositionInfoSource::AccessError);
+ break;
+ case QAbstractSocket::RemoteHostClosedError:
+ setError(QGeoPositionInfoSource::ClosedError);
+ break;
+ default:
+ qWarning() << "Connection failed! QAbstractSocket::SocketError" << error;
+ // TODO - introduce new type of error. TransportError?
+ setError(QGeoPositionInfoSource::UnknownSourceError);
+ break;
+ }
+}
+
+void NmeaSource::parseSourceParameter(const QString &source)
+{
+ if (source.startsWith(QStringLiteral("socket:"))) {
+ // This is a socket
+ connectSocket(source);
+ } else {
+ // Last chance - this can be serial device.
+ // Note: File is handled in a separate case.
+ addSerialDevice(source);
+ }
+}
+
+static QString tryFindSerialDevice(const QString &requestedPort)
+{
+ QString portName;
+ if (requestedPort.isEmpty()) {
+ const QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
+ qCDebug(lcNmea) << "Found" << ports.count() << "serial ports";
+ if (ports.isEmpty()) {
+ qWarning("nmea: No serial ports found");
+ return portName;
+ }
+
+ // Try to find a well-known device.
+ QSet<int> supportedDevices;
+ supportedDevices << 0x67b; // GlobalSat (BU-353S4 and probably others)
+ supportedDevices << 0xe8d; // Qstarz MTK II
+ for (const QSerialPortInfo& port : ports) {
+ if (port.hasVendorIdentifier() && supportedDevices.contains(port.vendorIdentifier())) {
+ portName = port.portName();
+ break;
+ }
+ }
+
+ if (portName.isEmpty()) {
+ qWarning("nmea: No known GPS device found.");
+ }
+ } else {
+ portName = requestedPort;
+ if (portName.startsWith(QStringLiteral("serial:")))
+ portName.remove(0, 7);
+ }
+ return portName;
+}
+
+void NmeaSource::addSerialDevice(const QString &requestedPort)
+{
+ m_sourceName = tryFindSerialDevice(requestedPort);
+ if (m_sourceName.isEmpty())
+ return;
+
+ m_dataSource = deviceContainer->serial(m_sourceName);
+ if (!m_dataSource)
+ return;
+
+ setDevice(m_dataSource.data());
+}
+
+void NmeaSource::setFileName(const QString &fileName)
+{
+ m_sourceName = fileName;
+
+ m_fileSource.reset(new QFile(fileName));
+ qCDebug(lcNmea) << "Opening file" << fileName;
+ if (!m_fileSource->open(QIODevice::ReadOnly)) {
+ qWarning("nmea: failed to open file %s", qPrintable(fileName));
+ m_fileSource.reset();
+ }
+
+ if (!m_fileSource)
+ return;
+
+ qCDebug(lcNmea) << "Opened successfully";
+
+ setDevice(m_fileSource.data());
+}
+
+void NmeaSource::connectSocket(const QString &source)
+{
+ const QUrl url(source);
+ const QString host = url.host();
+ const int port = url.port();
+ if (!host.isEmpty() && (port > 0)) {
+ m_socket.reset(new QTcpSocket);
+ // no need to explicitly connect to connected() signal
+ connect(m_socket.get(), &QTcpSocket::errorOccurred, this, &NmeaSource::onSocketError);
+ m_socket->connectToHost(host, port, QTcpSocket::ReadOnly);
+ m_sourceName = source;
+
+ setDevice(m_socket.data());
+ } else {
+ qWarning("nmea: incorrect socket parameters %s:%d", qPrintable(host), port);
+ }
+}
+
+class NmeaSatelliteSource : public QNmeaSatelliteInfoSource
+{
+public:
+ NmeaSatelliteSource(QObject *parent, const QVariantMap &parameters)
+ : QNmeaSatelliteInfoSource(parent)
+ {
+ const QString requestedPort = parameters.value(sourceParameterName).toString();
+ m_portName = tryFindSerialDevice(requestedPort);
+ if (m_portName.isEmpty())
+ return;
+
+ m_port = deviceContainer->serial(m_portName);
+ if (!m_port)
+ return;
+
+ setDevice(m_port.data());
+ }
+
+ ~NmeaSatelliteSource()
+ {
+ deviceContainer->releaseSerial(m_portName, m_port);
+ }
+
+ bool isValid() const { return !m_port.isNull(); }
+
+private:
+ QSharedPointer<QIOPipe> m_port;
+ QString m_portName;
+};
+
+/*!
+ \internal
+ Returns a local file name if file exists, or an empty string otherwise
+*/
+static QString extractLocalFileName(const QVariantMap &parameters)
+{
+ QString localFileName = parameters.value(sourceParameterName).toString();
+ if (localFileName.isEmpty())
+ return QString();
+
+ if (!QFile::exists(localFileName)) {
+ if (localFileName.startsWith(QStringLiteral("qrc:///")))
+ localFileName.remove(0, 7);
+ else if (localFileName.startsWith(QStringLiteral("file:///")))
+ localFileName.remove(0, 7);
+ else if (localFileName.startsWith(QStringLiteral("qrc:/")))
+ localFileName.remove(0, 5);
+
+ if (!QFile::exists(localFileName) && localFileName.startsWith(QLatin1Char('/')))
+ localFileName.remove(0, 1);
+ }
+ if (!QFile::exists(localFileName))
+ localFileName.prepend(QLatin1Char(':'));
+
+ const bool isLocalFile = QFile::exists(localFileName);
+ return isLocalFile ? localFileName : QString();
+}
+
+QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryNmea::positionInfoSource(QObject *parent, const QVariantMap &parameters)
+{
+ std::unique_ptr<NmeaSource> src = nullptr;
+
+ const QString localFileName = extractLocalFileName(parameters);
+ if (localFileName.isEmpty())
+ src = std::make_unique<NmeaSource>(parent, parameters); // use RealTimeMode
+ else
+ src = std::make_unique<NmeaSource>(parent, localFileName); // use SimulationMode
+
+ return (src && src->isValid()) ? src.release() : nullptr;
+}
+
+QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryNmea::satelliteInfoSource(QObject *parent, const QVariantMap &parameters)
+{
+ auto src = std::make_unique<NmeaSatelliteSource>(parent, parameters);
+ return src->isValid() ? src.release() : nullptr;
+}
+
+QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryNmea::areaMonitor(QObject *parent, const QVariantMap &parameters)
+{
+ Q_UNUSED(parent);
+ Q_UNUSED(parameters);
+ return nullptr;
+}
+
+QT_END_NAMESPACE
+
+#include "qgeopositioninfosourcefactory_nmea.moc"
diff --git a/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.h b/src/plugins/position/nmea/qgeopositioninfosourcefactory_nmea.h
index 5090a912..d7ac98aa 100644
--- a/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.h
+++ b/src/plugins/position/nmea/qgeopositioninfosourcefactory_nmea.h
@@ -37,13 +37,15 @@
**
****************************************************************************/
-#ifndef QGEOPOSITIONINFOSOURCEFACTORY_SERIALNMEA_H
-#define QGEOPOSITIONINFOSOURCEFACTORY_SERIALNMEA_H
+#ifndef QGEOPOSITIONINFOSOURCEFACTORY_NMEA_H
+#define QGEOPOSITIONINFOSOURCEFACTORY_NMEA_H
#include <QObject>
#include <QtPositioning/QGeoPositionInfoSourceFactory>
-class QGeoPositionInfoSourceFactorySerialNmea : public QObject, public QGeoPositionInfoSourceFactory
+QT_BEGIN_NAMESPACE
+
+class QGeoPositionInfoSourceFactoryNmea : public QObject, public QGeoPositionInfoSourceFactory
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/6.0"
@@ -56,4 +58,6 @@ public:
QGeoAreaMonitorSource *areaMonitor(QObject *parent, const QVariantMap &parameters) override;
};
+QT_END_NAMESPACE
+
#endif
diff --git a/src/plugins/position/serialnmea/qiopipe.cpp b/src/plugins/position/nmea/qiopipe.cpp
index 75cd923c..75cd923c 100644
--- a/src/plugins/position/serialnmea/qiopipe.cpp
+++ b/src/plugins/position/nmea/qiopipe.cpp
diff --git a/src/plugins/position/serialnmea/qiopipe_p.h b/src/plugins/position/nmea/qiopipe_p.h
index 66453e10..dd83588a 100644
--- a/src/plugins/position/serialnmea/qiopipe_p.h
+++ b/src/plugins/position/nmea/qiopipe_p.h
@@ -108,6 +108,7 @@ public:
QList<QPointer<QIOPipe>> childPipes;
};
+QT_END_NAMESPACE
+
#endif // QIOPIPE_P_H
-QT_END_NAMESPACE
diff --git a/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp b/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp
deleted file mode 100644
index 504d46b9..00000000
--- a/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp
+++ /dev/null
@@ -1,254 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtPositioning module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qgeopositioninfosourcefactory_serialnmea.h"
-#include <QtPositioning/qnmeapositioninfosource.h>
-#include <QtPositioning/qnmeasatelliteinfosource.h>
-#include <QtSerialPort/qserialport.h>
-#include <QtSerialPort/qserialportinfo.h>
-#include <QtCore/qloggingcategory.h>
-#include <QSet>
-#include "qiopipe_p.h"
-#include <QSharedPointer>
-
-Q_LOGGING_CATEGORY(lcSerial, "qt.positioning.serialnmea")
-
-class IODeviceContainer
-{
-public:
- IODeviceContainer() {}
- IODeviceContainer(IODeviceContainer const&) = delete;
- void operator=(IODeviceContainer const&) = delete;
-
- QSharedPointer<QIOPipe> serial(const QString &portName)
- {
- if (m_serialPorts.contains(portName)) {
- m_serialPorts[portName].refs++;
- QIOPipe *endPipe = new QIOPipe(m_serialPorts[portName].proxy);
- m_serialPorts[portName].proxy->addChildPipe(endPipe);
- return QSharedPointer<QIOPipe>(endPipe);
- }
- IODevice device;
- QSerialPort *port = new QSerialPort(portName);
- port->setBaudRate(4800);
- qCDebug(lcSerial) << "Opening serial port" << portName;
- if (!port->open(QIODevice::ReadOnly)) {
- qWarning("serialnmea: Failed to open %s", qPrintable(portName));
- delete port;
- return {};
- }
- qCDebug(lcSerial) << "Opened successfully";
- device.device = port;
- device.refs = 1;
- device.proxy = new QIOPipe(port, QIOPipe::ProxyPipe);
- m_serialPorts[portName] = device;
- QIOPipe *endPipe = new QIOPipe(device.proxy);
- device.proxy->addChildPipe(endPipe);
- return QSharedPointer<QIOPipe>(endPipe);
- }
-
- void releaseSerial(const QString &portName, QSharedPointer<QIOPipe> &pipe) {
- if (!m_serialPorts.contains(portName))
- return;
-
- pipe.clear(); // make sure to release the pipe returned by getSerial, or else, if there are still refs, data will be leaked through it
- IODevice &device = m_serialPorts[portName];
- if (device.refs > 1) {
- device.refs--;
- return;
- }
-
- IODevice taken = m_serialPorts.take(portName);
- taken.device->deleteLater();
- }
-
-private:
-
- struct IODevice {
- QIODevice *device = nullptr;
- QIOPipe *proxy = nullptr; // adding client pipes as children of proxy allows to dynamically add clients to one device.
- unsigned int refs = 1;
- };
-
- QMap<QString, IODevice> m_serialPorts;
-};
-
-Q_GLOBAL_STATIC(IODeviceContainer, deviceContainer)
-
-
-class NmeaSource : public QNmeaPositionInfoSource
-{
-public:
- explicit NmeaSource(QObject *parent, const QVariantMap &parameters);
- ~NmeaSource() override;
- bool isValid() const { return !m_port.isNull(); }
-
-private:
- QSharedPointer<QIOPipe> m_port;
- QString m_portName;
-};
-
-NmeaSource::NmeaSource(QObject *parent, const QVariantMap &parameters)
- : QNmeaPositionInfoSource(RealTimeMode, parent)
-{
- QByteArray requestedPort;
- if (parameters.contains(QStringLiteral("serialnmea.serial_port")))
- requestedPort = parameters.value(QStringLiteral("serialnmea.serial_port")).toString().toLatin1();
- else
- requestedPort = qgetenv("QT_NMEA_SERIAL_PORT");
- QString portName;
- if (requestedPort.isEmpty()) {
- const QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
- qCDebug(lcSerial) << "Found" << ports.count() << "serial ports";
- if (ports.isEmpty()) {
- qWarning("serialnmea: No serial ports found");
- return;
- }
-
- // Try to find a well-known device.
- QSet<int> supportedDevices;
- supportedDevices << 0x67b; // GlobalSat (BU-353S4 and probably others)
- supportedDevices << 0xe8d; // Qstarz MTK II
- for (const QSerialPortInfo& port : ports) {
- if (port.hasVendorIdentifier() && supportedDevices.contains(port.vendorIdentifier())) {
- portName = port.portName();
- break;
- }
- }
-
- if (portName.isEmpty()) {
- qWarning("serialnmea: No known GPS device found. Specify the COM port via QT_NMEA_SERIAL_PORT.");
- return;
- }
- m_portName = portName;
- } else {
- m_portName = QString::fromUtf8(requestedPort);
- }
-
- m_port = deviceContainer->serial(m_portName);
- if (!m_port)
- return;
-
- setDevice(m_port.data());
-}
-
-NmeaSource::~NmeaSource()
-{
- deviceContainer->releaseSerial(m_portName, m_port);
-}
-
-
-
-class NmeaSatelliteSource : public QNmeaSatelliteInfoSource
-{
-public:
- NmeaSatelliteSource(QObject *parent, const QVariantMap &parameters)
- : QNmeaSatelliteInfoSource(parent)
- {
- QByteArray requestedPort;
- if (parameters.contains(QStringLiteral("serialnmea.serial_port")))
- requestedPort = parameters.value(QStringLiteral("serialnmea.serial_port")).toString().toLatin1();
- else
- requestedPort = qgetenv("QT_NMEA_SERIAL_PORT");
- QString portName;
- if (requestedPort.isEmpty()) {
- const QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
- qCDebug(lcSerial) << "Found" << ports.count() << "serial ports";
- if (ports.isEmpty()) {
- qWarning("serialnmea: No serial ports found");
- return;
- }
-
- // Try to find a well-known device.
- QSet<int> supportedDevices;
- supportedDevices << 0x67b; // GlobalSat (BU-353S4 and probably others)
- supportedDevices << 0xe8d; // Qstarz MTK II
- foreach (const QSerialPortInfo& port, ports) {
- if (port.hasVendorIdentifier() && supportedDevices.contains(port.vendorIdentifier())) {
- portName = port.portName();
- break;
- }
- }
-
- if (portName.isEmpty()) {
- qWarning("serialnmea: No known GPS device found. Specify the COM port via QT_NMEA_SERIAL_PORT.");
- return;
- }
- m_portName = portName;
- } else {
- m_portName = QString::fromUtf8(requestedPort);
- }
-
- m_port = deviceContainer->serial(m_portName);
- if (!m_port)
- return;
-
- setDevice(m_port.data());
- }
-
- ~NmeaSatelliteSource()
- {
- deviceContainer->releaseSerial(m_portName, m_port);
- }
-
- bool isValid() const { return !m_port.isNull(); }
-
-private:
- QSharedPointer<QIOPipe> m_port;
- QString m_portName;
-};
-
-QGeoPositionInfoSource *QGeoPositionInfoSourceFactorySerialNmea::positionInfoSource(QObject *parent, const QVariantMap &parameters)
-{
- QScopedPointer<NmeaSource> src(new NmeaSource(parent, parameters));
- return src->isValid() ? src.take() : nullptr;
-}
-
-QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactorySerialNmea::satelliteInfoSource(QObject *parent, const QVariantMap &parameters)
-{
- QScopedPointer<NmeaSatelliteSource> src(new NmeaSatelliteSource(parent, parameters));
- return src->isValid() ? src.take() : nullptr;
-}
-
-QGeoAreaMonitorSource *QGeoPositionInfoSourceFactorySerialNmea::areaMonitor(QObject *parent, const QVariantMap &parameters)
-{
- Q_UNUSED(parent);
- Q_UNUSED(parameters);
- return nullptr;
-}
diff --git a/src/plugins/position/serialnmea/serialnmea.pro b/src/plugins/position/serialnmea/serialnmea.pro
deleted file mode 100644
index 4e0088bb..00000000
--- a/src/plugins/position/serialnmea/serialnmea.pro
+++ /dev/null
@@ -1,16 +0,0 @@
-TARGET = qtposition_serialnmea
-
-QT = core-private positioning-private serialport
-
-HEADERS += \
- qgeopositioninfosourcefactory_serialnmea.h qnmeasatelliteinfosource_p.h qiopipe_p.h
-
-SOURCES += \
- qgeopositioninfosourcefactory_serialnmea.cpp qnmeasatelliteinfosource.cpp qiopipe.cpp
-
-OTHER_FILES += \
- plugin.json
-
-PLUGIN_TYPE = position
-PLUGIN_CLASS_NAME = QGeoPositionInfoSourceFactorySerialNmea
-load(qt_plugin)
diff --git a/src/positioning/doc/src/plugins/nmea.qdoc b/src/positioning/doc/src/plugins/nmea.qdoc
new file mode 100644
index 00000000..44b5f86d
--- /dev/null
+++ b/src/positioning/doc/src/plugins/nmea.qdoc
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+\page position-plugin-nmea.html
+\title Qt Positioning NMEA plugin
+\ingroup QtPositioning-plugins
+
+\brief Reads the NMEA stream to provide position updates.
+
+\section1 Overview
+
+Included with Qt Positioning is a position plugin which parses NMEA sentences
+into position updates. This plugin can use serial port, socket or file as a
+source.
+
+This plugin can be loaded by using the provider name \b nmea.
+
+\section1 Parameters
+
+The following table lists parameters that \e can be passed to the nmea plugin.
+
+\table
+\header
+ \li Parameter
+ \li Description
+\row
+ \li nmea.source
+ \li The source that will be used to get NMEA data.
+\endtable
+
+Different sources require different ways of providing the data. The following
+table lists different ways of providing \c {nmea.source} parameter for socket,
+serial port and file inputs.
+
+\table
+\header
+ \li Scheme
+ \li Example
+ \li Description
+\row
+ \li socket://hostname:port
+ \li \c {socket://localhost:12345}
+ \li Use \b {socket:} keyword to specify that you want to get the nmea data
+ from the socket. A TCP socket will be created, which will try to connect
+ to host \c hostname using port \c port. Upon successful connection
+ a text NMEA stream is expected to be received from the server.
+\row
+ \li {1, 3} serial:portname
+ \li \c {serial:/dev/ttyUSB0}
+ \li {1, 3} Use \b {serial:} keyword to specify that you want to get the nmea
+ data from the serial port. The plugin will try to establish a connection
+ to port \c portname with baudrate = 4800 Bd. Upon successful connection
+ a text NMEA stream is expected to be received from the serial port.
+ If you use \b {serial:} without any port name, the plugin will try to
+ find one of the well known serial devices using vendor identifier. Note
+ however that this is not a recommended way of using the serial port
+ connection, as the list of well-known devices is small and most probably
+ does not include your hardware.
+\row
+ \li \c {serial:COM1}
+\row
+ \li \c {serial:}
+\row
+ \li filepath
+ \li \c {/home/user/nmealog.txt}
+ \li {1, 2} Use \b {file:///} or just full file path to specify a path to a
+ local file.
+\row
+ \li file:///filepath
+ \li \c {file:///home/user/nmealog.txt}
+\row
+ \li qrc:///filepath
+ \li \c {qrc:///nmealog.txt}
+ \li Use \b {qrc:///} prefix to specify a path to a file in the application
+ resources.
+\endtable
+
+\note If \c {nmea.source} parameter is not specified, the plugin will try to
+locate one of the well-known serial devices (as if \c {nmea.source = serial:}
+was specified).
+
+\section1 Parameter Usage Example
+
+The following examples show how to create a \b nmea PositionSource
+using different data sources.
+
+\section2 QML
+
+\code
+// text file
+PositionSource {
+ name: "nmea"
+ PluginParameter { name: "nmea.source"; value: "qrc:///nmealog.txt" }
+}
+
+// socket
+PositionSource {
+ name: "nmea"
+ PluginParameter { name: "nmea.source"; value: "socket://localhost:22222" }
+}
+
+// serial port
+PositionSource {
+ name: "nmea"
+ PluginParameter { name: "nmea.source"; value: "serial:/dev/ttyACM0" }
+}
+\endcode
+
+\section2 C++
+
+\code
+// text file
+QVariantMap params;
+params["nmea.source"] = "qrc:///nmealog.txt";
+QGeoPositionInfoSource *textPositionSource = QGeoPositionInfoSource::createSource("nmea", params, this);
+
+// socket
+params["nmea.source"] = "socket://localhost:22222";
+QGeoPositionInfoSource *socketPositionSource = QGeoPositionInfoSource::createSource("nmea", params, this);
+
+// serial port
+params["nmea.source"] = "serial:/dev/ttyACM0";
+QGeoPositionInfoSource *serialPositionSource = QGeoPositionInfoSource::createSource("nmea", params, this);
+\endcode
+
+\note Once a PositionSource is created, it can't be reconfigured to use other
+type of source data.
+
+*/
diff --git a/src/positioning/doc/src/plugins/serialnmea.qdoc b/src/positioning/doc/src/plugins/serialnmea.qdoc
deleted file mode 100644
index 3e9861b5..00000000
--- a/src/positioning/doc/src/plugins/serialnmea.qdoc
+++ /dev/null
@@ -1,78 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
-\page position-plugin-serialnmea.html
-\title Qt Positioning Serial NMEA plugin
-\ingroup QtPositioning-plugins
-
-\brief Reads the NMEA stream from a serial connection to provide position updates.
-
-\section1 Overview
-
-Included with Qt Location is a position plugin which parses NMEA sentences streamed
-over a serial port into position updates.
-
-This plugin can be loaded by using the provider name \b serialnmea.
-
-
-\section1 Parameters
-
-The following table lists parameters that \e can be passed to the serialnmea plugin.
-
-\table
-\header
- \li Parameter
- \li Description
-\row
- \li serialnmea.serial_port
- \li The serial port where the NMEA stream is coming.
-\endtable
-
-\section1 Parameter Usage Example
-
-The following examples show how to create a \b serialnmea PositionSource
-listening on a specific serial port
-
-\section2 QML
-
-\code
-PositionSource {
- name: "serialnmea"
- PluginParameter { name: "serialnmea.serial_port"; value: "tnt1" }
-}
-\endcode
-
-\section2 C++
-
-\code
-QVariantMap params;
-params["serialnmea.serial_port"] = "tnt1";
-QGeoPositionInfoSource *positionSource = QGeoPositionInfoSource::createSource("serialnmea", params, this);
-\endcode
-
-*/
diff --git a/src/positioning/doc/src/qtpositioning-plugins.qdoc b/src/positioning/doc/src/qtpositioning-plugins.qdoc
index d11c3121..efbcd0ea 100644
--- a/src/positioning/doc/src/qtpositioning-plugins.qdoc
+++ b/src/positioning/doc/src/qtpositioning-plugins.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -45,10 +45,10 @@ Some plugins already ship with Qt. These are:
\li Wraps iOS and macOS positioning subsystems. Available only on Apple platforms supporting corelocation.
\row
\li \b geoclue
- \li Interfaces with \l{https://www.freedesktop.org/wiki/Software/GeoClue/}{GeoClue} v0.12. Requires GeoClue 0.12 to be present to function.
+ \li Interfaces with \l{https://gitlab.freedesktop.org/geoclue/geoclue/-/wikis/home}{GeoClue} v0.12. Requires GeoClue 0.12 to be present to function.
\row
\li \b geoclue2
- \li Interfaces with \l{https://www.freedesktop.org/wiki/Software/GeoClue/}{GeoClue} v2. Requires GeoClue v2 to be present to function.
+ \li Interfaces with \l{https://gitlab.freedesktop.org/geoclue/geoclue/-/wikis/home}{GeoClue} v2. Requires GeoClue v2 to be present to function.
\row
\li \b gypsy
\li Interfaces with \l{https://gypsy.freedesktop.org/wiki/}{Gypsy} daemon. Requires Gypsy to be present to function.
@@ -56,9 +56,10 @@ Some plugins already ship with Qt. These are:
\li \b winrt
\li Wraps WinRT positioning subsystem. Available only on WinRT and Windows10.
\row
- \li \b serialnmea
- \li A \l {Qt Positioning Serial NMEA plugin}{Serial NMEA} backend that parses NMEA streams from a GPS receiver over a
- serial link to provide position updates.
+ \li \b nmea
+ \li A \l {Qt Positioning NMEA plugin}{NMEA} backend that parses NMEA
+ streams from a GPS receiver to provide position updates. This plugin can
+ use serial port, socket or file as a source.
\row
\li \b positionpoll
\li A backend providing only area monitoring functionalities via polling on position updates.
diff --git a/src/positioning/qnmeapositioninfosource.h b/src/positioning/qnmeapositioninfosource.h
index 168a63d7..79a52361 100644
--- a/src/positioning/qnmeapositioninfosource.h
+++ b/src/positioning/qnmeapositioninfosource.h
@@ -84,12 +84,12 @@ protected:
int size,
QGeoPositionInfo *posInfo,
bool *hasFix);
+ void setError(QGeoPositionInfoSource::Error positionError);
private:
Q_DISABLE_COPY(QNmeaPositionInfoSource)
friend class QNmeaPositionInfoSourcePrivate;
QNmeaPositionInfoSourcePrivate *d;
- void setError(QGeoPositionInfoSource::Error positionError);
};
QT_END_NAMESPACE