diff options
author | Ivan Solovev <ivan.solovev@qt.io> | 2021-02-09 11:44:14 +0100 |
---|---|---|
committer | Ivan Solovev <ivan.solovev@qt.io> | 2021-02-12 10:47:07 +0100 |
commit | d8095d6875056f0a5926801adc57bbfe00d9d24f (patch) | |
tree | df831928a7f2e319cf7cf3ba788f4727a3fbc1a2 | |
parent | 4ca9fd760f1511e00301a8f13c10108fe83823e7 (diff) | |
download | qtlocation-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>
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 ¶meters); + 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 ¶meters) + : 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 ¶meters) + : 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 ¶meters) +{ + 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 ¶meters) +{ + 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 ¶meters) +{ + auto src = std::make_unique<NmeaSatelliteSource>(parent, parameters); + return src->isValid() ? src.release() : nullptr; +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryNmea::areaMonitor(QObject *parent, const QVariantMap ¶meters) +{ + 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 ¶meters) 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 ¶meters); - ~NmeaSource() override; - bool isValid() const { return !m_port.isNull(); } - -private: - QSharedPointer<QIOPipe> m_port; - QString m_portName; -}; - -NmeaSource::NmeaSource(QObject *parent, const QVariantMap ¶meters) - : 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 ¶meters) - : 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 ¶meters) -{ - QScopedPointer<NmeaSource> src(new NmeaSource(parent, parameters)); - return src->isValid() ? src.take() : nullptr; -} - -QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactorySerialNmea::satelliteInfoSource(QObject *parent, const QVariantMap ¶meters) -{ - QScopedPointer<NmeaSatelliteSource> src(new NmeaSatelliteSource(parent, parameters)); - return src->isValid() ? src.take() : nullptr; -} - -QGeoAreaMonitorSource *QGeoPositionInfoSourceFactorySerialNmea::areaMonitor(QObject *parent, const QVariantMap ¶meters) -{ - 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 |