summaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-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
4 files changed, 397 insertions, 23 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