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>
34#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
45class IODeviceContainer
48 IODeviceContainer() {}
49 IODeviceContainer(IODeviceContainer
const&) =
delete;
50 void operator=(IODeviceContainer
const&) =
delete;
52 QSharedPointer<QIOPipe> serial(
const QString &portName, qint32 baudRate)
54 if (m_serialPorts.contains(portName)) {
55 m_serialPorts[portName].refs++;
56 QIOPipe *endPipe =
new QIOPipe(m_serialPorts[portName].proxy);
57 m_serialPorts[portName].proxy->addChildPipe(endPipe);
58 return QSharedPointer<QIOPipe>(endPipe);
61 QSerialPort *port =
new QSerialPort(portName);
62 port->setBaudRate(baudRate);
63 qCDebug(lcNmea) <<
"Opening serial port" << portName <<
"with baudrate" << baudRate;
64 if (!port->open(QIODevice::ReadOnly)) {
65 qWarning(
"nmea: Failed to open %s", qPrintable(portName));
69 qCDebug(lcNmea) <<
"Opened successfully";
72 device.proxy =
new QIOPipe(port, QIOPipe::ProxyPipe);
73 m_serialPorts[portName] = device;
74 QIOPipe *endPipe =
new QIOPipe(device.proxy);
75 device.proxy->addChildPipe(endPipe);
76 return QSharedPointer<QIOPipe>(endPipe);
79 void releaseSerial(
const QString &portName, QSharedPointer<QIOPipe> &pipe)
81 if (!m_serialPorts.contains(portName))
85 IODevice &device = m_serialPorts[portName];
86 if (device.refs > 1) {
91 IODevice taken = m_serialPorts.take(portName);
92 taken.device->deleteLater();
98 QIODevice *device =
nullptr;
99 QIOPipe *proxy =
nullptr;
101 unsigned int refs = 1;
104 QMap<QString, IODevice> m_serialPorts;
107Q_GLOBAL_STATIC(IODeviceContainer, deviceContainer)
121 source = parameters.value(sourceParameterName).toString();
123 const auto br = parameters.value(baudRateParameterName).toInt(&ok);
147 return !m_dataSource.isNull() || m_fileSource || m_socket;
155 void addSerialDevice(
const QString &requestedPort, quint32 baudRate);
156 void setFileName(
const QString &fileName);
157 void connectSocket(
const QString &source);
159 QSharedPointer<QIOPipe> m_dataSource;
160 std::unique_ptr<QFile> m_fileSource;
161 std::unique_ptr<QTcpSocket> m_socket;
162 QString m_sourceName;
174 setFileName(fileName);
179#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
180 if (deviceContainer.exists())
181 deviceContainer->releaseSerial(m_sourceName, m_dataSource);
185void NmeaSource::onSocketError(QAbstractSocket::SocketError error)
190 case QAbstractSocket::UnknownSocketError:
191 setError(QGeoPositionInfoSource::UnknownSourceError);
193 case QAbstractSocket::SocketAccessError:
194 setError(QGeoPositionInfoSource::AccessError);
196 case QAbstractSocket::RemoteHostClosedError:
197 setError(QGeoPositionInfoSource::ClosedError);
200 qWarning() <<
"Connection failed! QAbstractSocket::SocketError" << error;
202 setError(QGeoPositionInfoSource::UnknownSourceError);
209 if (parameters.source.startsWith(socketScheme)) {
211 connectSocket(parameters.source);
215 addSerialDevice(parameters.source, parameters.baudRate);
219#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
220static QString tryFindSerialDevice(
const QString &requestedPort)
223 if (requestedPort.isEmpty()) {
224 const QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
225 qCDebug(lcNmea) <<
"Found" << ports.size() <<
"serial ports";
226 if (ports.isEmpty()) {
227 qWarning(
"nmea: No serial ports found");
232 QSet<
int> supportedDevices;
233 supportedDevices << 0x67b;
234 supportedDevices << 0xe8d;
235 for (
const QSerialPortInfo& port : ports) {
236 if (port.hasVendorIdentifier() && supportedDevices.contains(port.vendorIdentifier())) {
237 portName = port.portName();
242 if (portName.isEmpty()) {
243 qWarning(
"nmea: No known GPS device found.");
246 portName = requestedPort;
247 if (portName.startsWith(serialScheme))
248 portName.remove(0, 7);
254void NmeaSource::addSerialDevice(
const QString &requestedPort, quint32 baudRate)
256#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
257 m_sourceName = tryFindSerialDevice(requestedPort);
258 if (m_sourceName.isEmpty())
261 m_dataSource = deviceContainer->serial(m_sourceName, baudRate);
265 setDevice(m_dataSource.data());
270 qWarning() <<
"Plugin was built without serialport support!"
271 << requestedPort <<
"cannot be used!";
275void NmeaSource::setFileName(
const QString &fileName)
277 m_sourceName = fileName;
279 m_fileSource.reset(
new QFile(fileName));
280 qCDebug(lcNmea) <<
"Opening file" << fileName;
281 if (!m_fileSource->open(QIODevice::ReadOnly)) {
282 qWarning(
"nmea: failed to open file %s", qPrintable(fileName));
283 m_fileSource.reset();
289 qCDebug(lcNmea) <<
"Opened successfully";
291 setDevice(m_fileSource.get());
294void NmeaSource::connectSocket(
const QString &source)
296 const QUrl url(source);
297 const QString host = url.host();
298 const int port = url.port();
299 if (!host.isEmpty() && (port > 0)) {
300 m_socket.reset(
new QTcpSocket);
302 connect(m_socket.get(), &QTcpSocket::errorOccurred,
this, &NmeaSource::onSocketError);
303 m_socket->connectToHost(host, port, QTcpSocket::ReadOnly);
304 m_sourceName = source;
306 setDevice(m_socket.get());
308 qWarning(
"nmea: incorrect socket parameters %s:%d", qPrintable(host), port);
320 bool isValid()
const {
return !m_port.isNull() || m_file || m_socket; }
327 void parseSimulationSource(
const QString &localFileName);
329 QSharedPointer<QIOPipe> m_port;
330 std::unique_ptr<QFile> m_file;
331 std::unique_ptr<QTcpSocket> m_socket;
332 QString m_sourceName;
336 : QNmeaSatelliteInfoSource(QNmeaSatelliteInfoSource::UpdateMode::RealTimeMode, parent)
346 const QVariantMap ¶meters)
351 parameters.value(QNmeaSatelliteInfoSource::SimulationUpdateInterval).toInt(&ok);
353 setBackendProperty(QNmeaSatelliteInfoSource::SimulationUpdateInterval, interval);
354 parseSimulationSource(fileName);
359#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
360 if (deviceContainer.exists())
361 deviceContainer->releaseSerial(m_sourceName, m_port);
370 case QAbstractSocket::UnknownSocketError:
371 setError(QGeoSatelliteInfoSource::UnknownSourceError);
373 case QAbstractSocket::SocketAccessError:
374 setError(QGeoSatelliteInfoSource::AccessError);
376 case QAbstractSocket::RemoteHostClosedError:
377 setError(QGeoSatelliteInfoSource::ClosedError);
380 qWarning() <<
"Connection failed! QAbstractSocket::SocketError" << error;
382 setError(QGeoSatelliteInfoSource::UnknownSourceError);
389 const QString source = parameters.source;
390 if (source.startsWith(socketScheme)) {
392 const QUrl url(source);
393 const QString host = url.host();
394 const int port = url.port();
395 if (!host.isEmpty() && (port > 0)) {
396 m_socket.reset(
new QTcpSocket);
398 connect(m_socket.get(), &QTcpSocket::errorOccurred,
399 this, &NmeaSatelliteSource::onSocketError);
400 m_socket->connectToHost(host, port, QTcpSocket::ReadOnly);
401 m_sourceName = source;
403 setDevice(m_socket.get());
405 qWarning(
"nmea: incorrect socket parameters %s:%d", qPrintable(host), port);
408#ifdef QT_NMEA_PLUGIN_HAS_SERIALPORT
410 m_sourceName = tryFindSerialDevice(source);
411 if (m_sourceName.isEmpty())
414 m_port = deviceContainer->serial(m_sourceName, parameters.baudRate);
418 setDevice(m_port.data());
422 qWarning() <<
"Plugin was built without serialport support!"
423 << source <<
"cannot be used!";
431 m_sourceName = localFileName;
433 qCDebug(lcNmea) <<
"Opening file" << localFileName;
434 m_file.reset(
new QFile(localFileName));
435 if (!m_file->open(QIODevice::ReadOnly)) {
436 qWarning(
"nmea: failed to open file %s", qPrintable(localFileName));
440 qCDebug(lcNmea) <<
"Opened successfully";
442 setDevice(m_file.get());
446
447
448
449
450
453 if (source.isEmpty())
456 QString localFileName = source;
458 if (!QFile::exists(localFileName)) {
459 if (localFileName.startsWith(QStringLiteral(
"qrc:///")))
460 localFileName.remove(0, 7);
461 else if (localFileName.startsWith(QStringLiteral(
"file:///")))
462 localFileName.remove(0, 7);
463 else if (localFileName.startsWith(QStringLiteral(
"qrc:/")))
464 localFileName.remove(0, 5);
466 if (!QFile::exists(localFileName) && localFileName.startsWith(QLatin1Char(
'/')))
467 localFileName.remove(0, 1);
469 if (!QFile::exists(localFileName))
470 localFileName.prepend(QLatin1Char(
':'));
472 const bool isLocalFile = QFile::exists(localFileName);
473 return isLocalFile ? localFileName : QString();
477
478
479
482 QString localFileName = parameters.value(sourceParameterName).toString();
483 return checkSourceIsFile(localFileName);
486QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryNmea::positionInfoSource(QObject *parent,
const QVariantMap ¶meters)
488 std::unique_ptr<NmeaSource> src =
nullptr;
490 const QString localFileName = extractLocalFileName(parameters);
491 if (localFileName.isEmpty())
496 return (src && src->isValid()) ? src.release() :
nullptr;
499QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryNmea::satelliteInfoSource(QObject *parent,
const QVariantMap ¶meters)
501 std::unique_ptr<NmeaSatelliteSource> src =
nullptr;
503 const QString localFileName = extractLocalFileName(parameters);
504 if (localFileName.isEmpty()) {
511 return (src && src->isValid()) ? src.release() :
nullptr;
514QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryNmea::areaMonitor(QObject *parent,
const QVariantMap ¶meters)
517 Q_UNUSED(parameters);
523#include "moc_qgeopositioninfosourcefactory_nmea.cpp"
524#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)
static QT_BEGIN_NAMESPACE const auto sourceParameterName
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
NmeaParameters(const QVariantMap ¶meters)