diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp | 100 | ||||
-rw-r--r-- | src/plugins/position/serialnmea/qiopipe.cpp | 202 | ||||
-rw-r--r-- | src/plugins/position/serialnmea/qiopipe_p.h | 112 | ||||
-rw-r--r-- | src/plugins/position/serialnmea/serialnmea.pro | 6 |
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 ¶meters); + explicit NmeaSource(QObject *parent, const QVariantMap ¶meters); + ~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 ¶meters) - : 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 ¶meters) 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 ¶meters) 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 |