6#include <QtPositioning/QNmeaPositionInfoSource>
7#include <QtPositioning/QNmeaSatelliteInfoSource>
8#include <QtNetwork/QTcpSocket>
9#include <QLoggingCategory>
13#include <QSharedPointer>
16#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
17# include <QtSerialPort/QSerialPort>
18# include <QtSerialPort/QSerialPortInfo>
24Q_STATIC_LOGGING_CATEGORY(lcNmea,
"qt.positioning.nmea")
26static const auto sourceParameterName = QStringLiteral(
"nmea.source");
33#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
44class IODeviceContainer
47 IODeviceContainer() {}
48 IODeviceContainer(IODeviceContainer
const&) =
delete;
49 void operator=(IODeviceContainer
const&) =
delete;
51 QSharedPointer<QIOPipe> serial(
const QString &portName, qint32 baudRate)
53 if (m_serialPorts.contains(portName)) {
54 m_serialPorts[portName].refs++;
55 QIOPipe *endPipe =
new QIOPipe(m_serialPorts[portName].proxy);
56 m_serialPorts[portName].proxy->addChildPipe(endPipe);
57 return QSharedPointer<QIOPipe>(endPipe);
60 QSerialPort *port =
new QSerialPort(portName);
61 port->setBaudRate(baudRate);
62 qCDebug(lcNmea) <<
"Opening serial port" << portName <<
"with baudrate" << baudRate;
63 if (!port->open(QIODevice::ReadOnly)) {
64 qWarning(
"nmea: Failed to open %s", qPrintable(portName));
68 qCDebug(lcNmea) <<
"Opened successfully";
71 device.proxy =
new QIOPipe(port, QIOPipe::ProxyPipe);
72 m_serialPorts[portName] = device;
73 QIOPipe *endPipe =
new QIOPipe(device.proxy);
74 device.proxy->addChildPipe(endPipe);
75 return QSharedPointer<QIOPipe>(endPipe);
78 void releaseSerial(
const QString &portName, QSharedPointer<QIOPipe> &pipe)
80 if (!m_serialPorts.contains(portName))
84 IODevice &device = m_serialPorts[portName];
85 if (device.refs > 1) {
90 IODevice taken = m_serialPorts.take(portName);
91 taken.device->deleteLater();
97 QIODevice *device =
nullptr;
98 QIOPipe *proxy =
nullptr;
100 unsigned int refs = 1;
103 QMap<QString, IODevice> m_serialPorts;
106Q_GLOBAL_STATIC(IODeviceContainer, deviceContainer)
120 source = parameters.value(sourceParameterName).toString();
122 const auto br = parameters.value(baudRateParameterName).toInt(&ok);
146 return !m_dataSource.isNull() || !m_fileSource.isNull() || !m_socket.isNull();
154 void addSerialDevice(
const QString &requestedPort, quint32 baudRate);
155 void setFileName(
const QString &fileName);
156 void connectSocket(
const QString &source);
158 QSharedPointer<QIOPipe> m_dataSource;
159 QScopedPointer<QFile> m_fileSource;
160 QScopedPointer<QTcpSocket> m_socket;
161 QString m_sourceName;
173 setFileName(fileName);
178#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
179 if (deviceContainer.exists())
180 deviceContainer->releaseSerial(m_sourceName, m_dataSource);
184void NmeaSource::onSocketError(QAbstractSocket::SocketError error)
189 case QAbstractSocket::UnknownSocketError:
190 setError(QGeoPositionInfoSource::UnknownSourceError);
192 case QAbstractSocket::SocketAccessError:
193 setError(QGeoPositionInfoSource::AccessError);
195 case QAbstractSocket::RemoteHostClosedError:
196 setError(QGeoPositionInfoSource::ClosedError);
199 qWarning() <<
"Connection failed! QAbstractSocket::SocketError" << error;
201 setError(QGeoPositionInfoSource::UnknownSourceError);
208 if (parameters.source.startsWith(socketScheme)) {
210 connectSocket(parameters.source);
214 addSerialDevice(parameters.source, parameters.baudRate);
218#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
219static QString tryFindSerialDevice(
const QString &requestedPort)
222 if (requestedPort.isEmpty()) {
223 const QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
224 qCDebug(lcNmea) <<
"Found" << ports.size() <<
"serial ports";
225 if (ports.isEmpty()) {
226 qWarning(
"nmea: No serial ports found");
231 QSet<
int> supportedDevices;
232 supportedDevices << 0x67b;
233 supportedDevices << 0xe8d;
234 for (
const QSerialPortInfo& port : ports) {
235 if (port.hasVendorIdentifier() && supportedDevices.contains(port.vendorIdentifier())) {
236 portName = port.portName();
241 if (portName.isEmpty()) {
242 qWarning(
"nmea: No known GPS device found.");
245 portName = requestedPort;
246 if (portName.startsWith(serialScheme))
247 portName.remove(0, 7);
253void NmeaSource::addSerialDevice(
const QString &requestedPort, quint32 baudRate)
255#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
256 m_sourceName = tryFindSerialDevice(requestedPort);
257 if (m_sourceName.isEmpty())
260 m_dataSource = deviceContainer->serial(m_sourceName, baudRate);
264 setDevice(m_dataSource.data());
269 qWarning() <<
"Plugin was built without serialport support!"
270 << requestedPort <<
"cannot be used!";
274void NmeaSource::setFileName(
const QString &fileName)
276 m_sourceName = fileName;
278 m_fileSource.reset(
new QFile(fileName));
279 qCDebug(lcNmea) <<
"Opening file" << fileName;
280 if (!m_fileSource->open(QIODevice::ReadOnly)) {
281 qWarning(
"nmea: failed to open file %s", qPrintable(fileName));
282 m_fileSource.reset();
288 qCDebug(lcNmea) <<
"Opened successfully";
290 setDevice(m_fileSource.data());
293void NmeaSource::connectSocket(
const QString &source)
295 const QUrl url(source);
296 const QString host = url.host();
297 const int port = url.port();
298 if (!host.isEmpty() && (port > 0)) {
299 m_socket.reset(
new QTcpSocket);
301 connect(m_socket.get(), &QTcpSocket::errorOccurred,
this, &NmeaSource::onSocketError);
302 m_socket->connectToHost(host, port, QTcpSocket::ReadOnly);
303 m_sourceName = source;
305 setDevice(m_socket.data());
307 qWarning(
"nmea: incorrect socket parameters %s:%d", qPrintable(host), port);
319 bool isValid()
const {
return !m_port.isNull() || !m_file.isNull() || !m_socket.isNull(); }
326 void parseSimulationSource(
const QString &localFileName);
328 QSharedPointer<QIOPipe> m_port;
329 QScopedPointer<QFile> m_file;
330 QScopedPointer<QTcpSocket> m_socket;
331 QString m_sourceName;
335 : QNmeaSatelliteInfoSource(QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode, parent)
345 const QVariantMap ¶meters)
350 parameters.value(QNmeaSatelliteInfoSource::SimulationUpdateInterval).toInt(&ok);
352 setBackendProperty(QNmeaSatelliteInfoSource::SimulationUpdateInterval, interval);
353 parseSimulationSource(fileName);
358#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
359 if (deviceContainer.exists())
360 deviceContainer->releaseSerial(m_sourceName, m_port);
369 case QAbstractSocket::UnknownSocketError:
370 setError(QGeoSatelliteInfoSource::UnknownSourceError);
372 case QAbstractSocket::SocketAccessError:
373 setError(QGeoSatelliteInfoSource::AccessError);
375 case QAbstractSocket::RemoteHostClosedError:
376 setError(QGeoSatelliteInfoSource::ClosedError);
379 qWarning() <<
"Connection failed! QAbstractSocket::SocketError" << error;
381 setError(QGeoSatelliteInfoSource::UnknownSourceError);
388 const QString source = parameters.source;
389 if (source.startsWith(socketScheme)) {
391 const QUrl url(source);
392 const QString host = url.host();
393 const int port = url.port();
394 if (!host.isEmpty() && (port > 0)) {
395 m_socket.reset(
new QTcpSocket);
397 connect(m_socket.get(), &QTcpSocket::errorOccurred,
398 this, &NmeaSatelliteSource::onSocketError);
399 m_socket->connectToHost(host, port, QTcpSocket::ReadOnly);
400 m_sourceName = source;
402 setDevice(m_socket.data());
404 qWarning(
"nmea: incorrect socket parameters %s:%d", qPrintable(host), port);
407#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
409 m_sourceName = tryFindSerialDevice(source);
410 if (m_sourceName.isEmpty())
413 m_port = deviceContainer->serial(m_sourceName, parameters.baudRate);
417 setDevice(m_port.data());
421 qWarning() <<
"Plugin was built without serialport support!"
422 << source <<
"cannot be used!";
430 m_sourceName = localFileName;
432 qCDebug(lcNmea) <<
"Opening file" << localFileName;
433 m_file.reset(
new QFile(localFileName));
434 if (!m_file->open(QIODevice::ReadOnly)) {
435 qWarning(
"nmea: failed to open file %s", qPrintable(localFileName));
439 qCDebug(lcNmea) <<
"Opened successfully";
441 setDevice(m_file.data());
445
446
447
448
449
452 if (source.isEmpty())
455 QString localFileName = source;
457 if (!QFile::exists(localFileName)) {
458 if (localFileName.startsWith(QStringLiteral(
"qrc:///")))
459 localFileName.remove(0, 7);
460 else if (localFileName.startsWith(QStringLiteral(
"file:///")))
461 localFileName.remove(0, 7);
462 else if (localFileName.startsWith(QStringLiteral(
"qrc:/")))
463 localFileName.remove(0, 5);
465 if (!QFile::exists(localFileName) && localFileName.startsWith(QLatin1Char(
'/')))
466 localFileName.remove(0, 1);
468 if (!QFile::exists(localFileName))
469 localFileName.prepend(QLatin1Char(
':'));
471 const bool isLocalFile = QFile::exists(localFileName);
472 return isLocalFile ? localFileName : QString();
476
477
478
481 QString localFileName = parameters.value(sourceParameterName).toString();
482 return checkSourceIsFile(localFileName);
485QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryNmea::positionInfoSource(QObject *parent,
const QVariantMap ¶meters)
487 std::unique_ptr<NmeaSource> src =
nullptr;
489 const QString localFileName = extractLocalFileName(parameters);
490 if (localFileName.isEmpty())
491 src = std::make_unique<NmeaSource>(parent, parameters);
493 src = std::make_unique<NmeaSource>(parent, localFileName);
495 return (src && src->isValid()) ? src.release() :
nullptr;
498QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryNmea::satelliteInfoSource(QObject *parent,
const QVariantMap ¶meters)
500 std::unique_ptr<NmeaSatelliteSource> src =
nullptr;
502 const QString localFileName = extractLocalFileName(parameters);
503 if (localFileName.isEmpty()) {
505 src = std::make_unique<NmeaSatelliteSource>(parent, parameters);
508 src = std::make_unique<NmeaSatelliteSource>(parent, localFileName, parameters);
510 return (src && src->isValid()) ? src.release() :
nullptr;
513QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryNmea::areaMonitor(QObject *parent,
const QVariantMap ¶meters)
516 Q_UNUSED(parameters);
522#include "moc_qgeopositioninfosourcefactory_nmea.cpp"
523#include "qgeopositioninfosourcefactory_nmea.moc"
NmeaSatelliteSource(QObject *parent, const QString &fileName, const QVariantMap ¶meters)
NmeaSource(QObject *parent, const QString &fileName)
static QString checkSourceIsFile(const QString &source)
static constexpr auto defaultBaudRate
static const auto socketScheme
static const auto serialScheme
static const auto baudRateParameterName
static QString extractLocalFileName(const QVariantMap ¶meters)
NmeaParameters(const QVariantMap ¶meters)