summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Angelelli <paolo.angelelli@qt.io>2019-04-05 12:36:22 +0200
committerpaolo <paolo.angelelli@qt.io>2019-08-09 13:49:42 +0200
commit3334c6f6d00255f6fd5690e4b9cb05416950fd4d (patch)
tree601dbf9cd3ac8ea2e500409a5c9ec90214580289
parent0a242cc026190432a115a82752ddf35b8be15de6 (diff)
downloadqtlocation-3334c6f6d00255f6fd5690e4b9cb05416950fd4d.tar.gz
Introduce QIOPipe and use it in serialnmea plugin
This allows the plugin to pipe the input stream into multiple, independent processing pipelines. Good for allowing multiple position sources from the same serial port, and will allow supporting satellite info source in the nmea plugin too. Change-Id: I21e3cd2c78e7138ca49821c68b55ce96eb83e986 Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
-rw-r--r--src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp100
-rw-r--r--src/plugins/position/serialnmea/qiopipe.cpp202
-rw-r--r--src/plugins/position/serialnmea/qiopipe_p.h112
-rw-r--r--src/plugins/position/serialnmea/serialnmea.pro6
-rw-r--r--src/positioning/positioning.pro3
5 files changed, 399 insertions, 24 deletions
diff --git a/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp b/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp
index 043f7682..bebf7230 100644
--- a/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp
+++ b/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp
@@ -43,34 +43,100 @@
#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:
- NmeaSource(QObject *parent, const QVariantMap &parameters);
+ explicit NmeaSource(QObject *parent, const QVariantMap &parameters);
+ ~NmeaSource() override;
bool isValid() const { return !m_port.isNull(); }
private:
- QScopedPointer<QSerialPort> m_port;
+ QSharedPointer<QIOPipe> m_port;
+ QString m_portName;
};
NmeaSource::NmeaSource(QObject *parent, const QVariantMap &parameters)
- : QNmeaPositionInfoSource(RealTimeMode, parent),
- m_port(new QSerialPort)
+ : 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");
- m_port.reset();
return;
}
@@ -78,8 +144,7 @@ NmeaSource::NmeaSource(QObject *parent, const QVariantMap &parameters)
QSet<int> supportedDevices;
supportedDevices << 0x67b; // GlobalSat (BU-353S4 and probably others)
supportedDevices << 0xe8d; // Qstarz MTK II
- QString portName;
- foreach (const QSerialPortInfo& port, ports) {
+ for (const QSerialPortInfo& port : ports) {
if (port.hasVendorIdentifier() && supportedDevices.contains(port.vendorIdentifier())) {
portName = port.portName();
break;
@@ -88,28 +153,23 @@ NmeaSource::NmeaSource(QObject *parent, const QVariantMap &parameters)
if (portName.isEmpty()) {
qWarning("serialnmea: No known GPS device found. Specify the COM port via QT_NMEA_SERIAL_PORT.");
- m_port.reset();
return;
}
-
- m_port->setPortName(portName);
+ m_portName = portName;
} else {
- m_port->setPortName(QString::fromUtf8(requestedPort));
+ m_portName = QString::fromUtf8(requestedPort);
}
- m_port->setBaudRate(4800);
-
- qCDebug(lcSerial) << "Opening serial port" << m_port->portName();
-
- if (!m_port->open(QIODevice::ReadOnly)) {
- qWarning("serialnmea: Failed to open %s", qPrintable(m_port->portName()));
- m_port.reset();
+ m_port = deviceContainer->serial(m_portName);
+ if (!m_port)
return;
- }
setDevice(m_port.data());
+}
- qCDebug(lcSerial) << "Opened successfully";
+NmeaSource::~NmeaSource()
+{
+ deviceContainer->releaseSerial(m_portName, m_port);
}
QGeoPositionInfoSource *QGeoPositionInfoSourceFactorySerialNmea::positionInfoSource(QObject *parent)
diff --git a/src/plugins/position/serialnmea/qiopipe.cpp b/src/plugins/position/serialnmea/qiopipe.cpp
new file mode 100644
index 00000000..ce908d4d
--- /dev/null
+++ b/src/plugins/position/serialnmea/qiopipe.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtLocation 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 "qiopipe_p.h"
+#include <QtCore/qmetaobject.h>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*
+ proxying means do *not* emit readyRead, and instead pump data
+ into child pipes directly in a zero-copy fashion.
+*/
+QIOPipePrivate::QIOPipePrivate(QIODevice *iodevice, bool proxying)
+ : m_proxying(proxying), source(iodevice)
+{
+ const QIOPipe *parentPipe = qobject_cast<QIOPipe *>(iodevice);
+ if (parentPipe && parentPipe->d_func()->m_proxying) // with proxying parent,
+ return; // don't do anything
+
+ // read available data, does not emit.
+ readAvailableData();
+ // connect readyRead to onReadyRead
+ QObjectPrivate::connect(source, &QIODevice::readyRead, this, &QIOPipePrivate::_q_onReadyRead);
+}
+
+QIOPipePrivate::~QIOPipePrivate()
+{
+}
+
+bool QIOPipePrivate::readAvailableData() {
+ if (!source)
+ return false;
+ QByteArray ba = source->readAll();
+ if (!ba.size())
+ return false;
+
+ pumpData(ba);
+ return true;
+}
+
+void QIOPipePrivate::pumpData(const QByteArray &ba)
+{
+ if (m_proxying) {
+ QVector<int> toRemove;
+ for (int i = 0; i < childPipes.size(); ++i) {
+ const QPointer<QIOPipe> &cp = childPipes.at(i);
+ if (!cp) {
+ toRemove.append(i);
+ continue;
+ }
+ QIOPipePrivate *cpp = cp->d_func();
+ cpp->pushData(ba);
+ }
+ for (int i = toRemove.size() - 1; i >= 0; --i) {
+ childPipes.remove(i);
+ }
+ } else {
+ for (int i = 0; i < readBuffers.size(); i++)
+ readBuffers[i].append(ba);
+ }
+}
+
+void QIOPipePrivate::pushData(const QByteArray &ba)
+{
+ Q_Q(QIOPipe);
+ if (!ba.size())
+ return;
+
+ pumpData(ba);
+ if (!m_proxying)
+ emit q->readyRead();
+}
+
+void QIOPipePrivate::_q_onReadyRead()
+{
+ Q_Q(QIOPipe);
+ if (readAvailableData() && !m_proxying)
+ emit q->readyRead();
+}
+
+void QIOPipePrivate::addChildPipe(QIOPipe *childPipe)
+{
+ if (childPipes.contains(childPipe))
+ return;
+ childPipes.append(childPipe);
+}
+
+void QIOPipePrivate::removeChildPipe(QIOPipe *childPipe)
+{
+ childPipes.removeOne(childPipe);
+}
+
+QIOPipe::QIOPipe(QIODevice *parent, Mode mode)
+ : QIODevice(*new QIOPipePrivate(parent, mode == ProxyPipe), parent)
+{
+ if (!parent->isOpen() && !parent->open(QIODevice::ReadOnly)) {
+ qWarning() << "QIOPipe: Failed to open " << parent;
+ return;
+ }
+ open(ReadOnly);
+}
+
+QIOPipe::~QIOPipe()
+{
+
+}
+
+bool QIOPipe::open(QIODevice::OpenMode mode)
+{
+ if (isOpen())
+ return true;
+
+ static const OpenMode supportedOpenMode = ReadOnly; // Currently limit it to read only
+ if (!(mode & supportedOpenMode)) {
+ qFatal("Unsupported open mode");
+ return false;
+ }
+
+ return QIODevice::open(mode);
+}
+
+bool QIOPipe::isSequential() const
+{
+ return true;
+}
+
+void QIOPipe::setReadChannelCount(int count)
+{
+ Q_D(QIOPipe);
+ d->setReadChannelCount(qMax(count, 1));
+}
+
+void QIOPipe::addChildPipe(QIOPipe *childPipe)
+{
+ Q_D(QIOPipe);
+ d->addChildPipe(childPipe);
+}
+
+/*!
+ \reimp
+
+ \omit
+ This function does not really read anything, as we use QIODevicePrivate's
+ buffer. The buffer will be read inside of QIODevice before this
+ method will be called.
+ See QIODevicePrivate::read, buffer.read(data, maxSize).
+ \endomit
+*/
+qint64 QIOPipe::readData(char *data, qint64 maxlen)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(maxlen);
+
+ // return 0 indicating there may be more data in the future
+ // Returning -1 means no more data in the future (end of stream).
+ return qint64(0);
+}
+
+qint64 QIOPipe::writeData(const char * /*data*/, qint64 /*len*/)
+{
+ qFatal("QIOPipe is a read-only device");
+ return qint64(0);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/position/serialnmea/qiopipe_p.h b/src/plugins/position/serialnmea/qiopipe_p.h
new file mode 100644
index 00000000..25758fcb
--- /dev/null
+++ b/src/plugins/position/serialnmea/qiopipe_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtLocation 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$
+**
+****************************************************************************/
+
+#ifndef QIOPIPE_P_H
+#define QIOPIPE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/private/qiodevice_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QObject;
+class QIOPipePrivate;
+
+class QIOPipe : public QIODevice
+{
+ Q_OBJECT
+
+public:
+ enum Mode {
+ EndPipe = 0x0000,
+ ProxyPipe = 0x0001
+ };
+
+ explicit QIOPipe(QIODevice *parent, Mode mode = EndPipe);
+ ~QIOPipe() override;
+
+ bool open(OpenMode openMode) override;
+ bool isSequential() const override;
+ void setReadChannelCount(int count);
+ void addChildPipe(QIOPipe *childPipe);
+
+protected:
+ qint64 readData(char *data, qint64 maxlen) override;
+ qint64 writeData(const char *data, qint64 len) override;
+
+private:
+ Q_DECLARE_PRIVATE(QIOPipe)
+ Q_DISABLE_COPY(QIOPipe)
+};
+
+class QIOPipePrivate : public QIODevicePrivate
+{
+ Q_DECLARE_PUBLIC(QIOPipe)
+
+public:
+ explicit QIOPipePrivate(QIODevice *iodevice, bool proxying);
+ ~QIOPipePrivate() override;
+
+ bool readAvailableData();
+ void pumpData(const QByteArray &ba);
+ void pushData(const QByteArray &ba);
+ void _q_onReadyRead();
+ void addChildPipe(QIOPipe *childPipe);
+ void removeChildPipe(QIOPipe *childPipe);
+
+ bool m_proxying = false;
+ QPointer<QIODevice> source;
+ QVector<QPointer<QIOPipe>> childPipes;
+};
+
+#endif // QIOPIPE_P_H
+
+QT_END_NAMESPACE
diff --git a/src/plugins/position/serialnmea/serialnmea.pro b/src/plugins/position/serialnmea/serialnmea.pro
index bdeb3f13..e5677e99 100644
--- a/src/plugins/position/serialnmea/serialnmea.pro
+++ b/src/plugins/position/serialnmea/serialnmea.pro
@@ -1,12 +1,12 @@
TARGET = qtposition_serialnmea
-QT = core positioning serialport
+QT = core-private positioning-private serialport
HEADERS += \
- qgeopositioninfosourcefactory_serialnmea.h
+ qgeopositioninfosourcefactory_serialnmea.h qiopipe_p.h
SOURCES += \
- qgeopositioninfosourcefactory_serialnmea.cpp
+ qgeopositioninfosourcefactory_serialnmea.cpp qiopipe.cpp
OTHER_FILES += \
plugin.json
diff --git a/src/positioning/positioning.pro b/src/positioning/positioning.pro
index 8996a7e4..1eff0ab2 100644
--- a/src/positioning/positioning.pro
+++ b/src/positioning/positioning.pro
@@ -59,6 +59,8 @@ PRIVATE_HEADERS += \
qgeopolygon_p.h \
qgeocoordinateobject_p.h \
qgeopositioninfo_p.h \
+ qgeosatelliteinfo_p.h \
+ qgeosatelliteinfosource_p.h \
qclipperutils_p.h
SOURCES += \
@@ -89,7 +91,6 @@ SOURCES += \
HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS
-
load(qt_module)
LIBS_PRIVATE += -L$$MODULE_BASE_OUTDIR/lib -lclip2tri$$qtPlatformTargetSuffix()