From 7afed04520900906107774bb0d06f68dd142602e Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 13 Nov 2015 17:21:43 -0800 Subject: [PATCH] Location service: upload GPS fixes to webservice With this Subsurface-mobile should be able to mostly replace the companion app. This needs some more testing and fine tuning (for example the minimum time / distance should be configurable, there should be a location name), but I think the hard part is done now. Signed-off-by: Dirk Hohndel --- qt-mobile/gpslocation.cpp | 71 +++++++++++++++++++++++++++++++++++++-- qt-mobile/gpslocation.h | 5 +++ qt-mobile/qml/main.qml | 7 ++++ qt-mobile/qmlmanager.cpp | 5 +++ 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/qt-mobile/gpslocation.cpp b/qt-mobile/gpslocation.cpp index f2bde130d..fc9767c65 100644 --- a/qt-mobile/gpslocation.cpp +++ b/qt-mobile/gpslocation.cpp @@ -4,8 +4,12 @@ #include "dive.h" #include "helpers.h" #include +#include #include #include +#include +#include +#include GpsLocation::GpsLocation(QObject *parent) { @@ -121,7 +125,7 @@ bool GpsLocation::applyLocations() for (int j = last; j < cnt; j++) { if (time_during_dive_with_offset(d, gpsTable[j].when, SAME_GROUP)) { if (verbose) - qDebug() << "processing gpsfix @" << get_dive_date_string(gpsTable[j].when) << + qDebug() << "processing gpsFix @" << get_dive_date_string(gpsTable[j].when) << "which is withing six hours of dive from" << get_dive_date_string(d->when) << "until" << get_dive_date_string(d->when + d->duration.seconds); @@ -131,7 +135,7 @@ bool GpsLocation::applyLocations() */ if (time_during_dive_with_offset(d, gpsTable[j].when, 0)) { if (verbose) - qDebug() << "gpsfix is during the dive, pick that one"; + qDebug() << "gpsFix is during the dive, pick that one"; copy_gps_location(gpsTable + j, d); changed = true; last = j; @@ -167,7 +171,7 @@ bool GpsLocation::applyLocations() last = j; break; } else { - /* ok, gpsfix is before, nextgpsfix is after */ + /* ok, gpsFix is before, nextgpsFix is after */ if (d->when - gpsTable[j].when <= gpsTable[j+1].when - (d->when + d->duration.seconds)) { if (verbose) qDebug() << "pick the one before as it's closer to the start"; @@ -215,3 +219,64 @@ void GpsLocation::clearGpsData() geoSettings->clear(); geoSettings->sync(); } + +void GpsLocation::postError(QNetworkReply::NetworkError error) +{ + status(QString("error when sending a GPS fix: %1").arg(reply->errorString())); +} + +void GpsLocation::uploadToServer() +{ + // we want to do this one at a time (the server prefers that) + QEventLoop loop; + QTimer timer; + timer.setSingleShot(true); + + QNetworkAccessManager *manager = new QNetworkAccessManager(qApp); + QUrl url("http://api.subsurface-divelog.org/api/dive/add/"); + int count = geoSettings->value("count", 0).toInt(); + for (int i = 0; i < count; i++) { + QDateTime dt; + QUrlQuery data; + if (geoSettings->contains(QString("gpsFix%1_uploaded").arg(i))) + continue; + time_t when = geoSettings->value(QString("gpsFix%1_time").arg(i), 0).toULongLong(); + dt.setTime_t(when); + qDebug() << dt.toString() << get_dive_date_string(when); + data.addQueryItem("login", prefs.userid); + data.addQueryItem("dive_date", dt.toString("yyyy-MM-dd")); + data.addQueryItem("dive_time", dt.toString("hh:mm")); + data.addQueryItem("dive_latitude", QString::number(geoSettings->value(QString("gpsFix%1_lat").arg(i)).toInt() / 1000000.0)); + data.addQueryItem("dive_longitude", QString::number(geoSettings->value(QString("gpsFix%1_lon").arg(i)).toInt() / 1000000.0)); + status(data.toString(QUrl::FullyEncoded).toUtf8()); + QNetworkRequest request; + request.setUrl(url); + request.setRawHeader("Accept", "text/json"); + request.setRawHeader("Content-type", "application/x-www-form-urlencoded"); + reply = manager->post(request, data.toString(QUrl::FullyEncoded).toUtf8()); + connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + // somehoe I cannot get this to work with the new connect syntax: + // connect(reply, &QNetworkReply::error, this, &GpsLocation::postError); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(postError(QNetworkReply::NetworkError))); + timer.start(10000); + loop.exec(); + if (timer.isActive()) { + timer.stop(); + if (reply->error() != QNetworkReply::NoError) { + QString response = reply->readAll(); + if (!response.contains("Duplicate entry")) { + status(QString("Server response:") + reply->readAll()); + break; + } + } + } else { + status("Uploading to server timed out"); + break; + } + reply->deleteLater(); + status(QString("completed sending gps fix %1 - response: ").arg(i) + reply->readAll()); + geoSettings->setValue(QString("gpsFix%1_uploaded").arg(i), 1); + } +} diff --git a/qt-mobile/gpslocation.h b/qt-mobile/gpslocation.h index a8437cca2..ff7aa1ea4 100644 --- a/qt-mobile/gpslocation.h +++ b/qt-mobile/gpslocation.h @@ -7,6 +7,7 @@ #include #include #include +#include class GpsLocation : QObject { @@ -21,6 +22,7 @@ private: QGeoPositionInfoSource *gpsSource; void status(QString msg); QSettings *geoSettings; + QNetworkReply *reply; signals: @@ -28,7 +30,10 @@ public slots: void serviceEnable(bool toggle); void newPosition(QGeoPositionInfo pos); void updateTimeout(); + void uploadToServer(); + void postError(QNetworkReply::NetworkError error); void clearGpsData(); + }; #endif // GPSLOCATION_H diff --git a/qt-mobile/qml/main.qml b/qt-mobile/qml/main.qml index d4b01cb33..f28a5795f 100644 --- a/qt-mobile/qml/main.qml +++ b/qt-mobile/qml/main.qml @@ -86,6 +86,13 @@ ApplicationWindow { } } + MenuItem { + text: "Send GPS data to server" + onTriggered: { + manager.sendGpsData(); + } + } + MenuItem { text: "Clear stored GPS data" onTriggered: { diff --git a/qt-mobile/qmlmanager.cpp b/qt-mobile/qmlmanager.cpp index 63de7d6e8..24a5fd8aa 100644 --- a/qt-mobile/qmlmanager.cpp +++ b/qt-mobile/qmlmanager.cpp @@ -158,6 +158,11 @@ void QMLManager::applyGpsData() locationProvider->applyLocations(); } +void QMLManager::sendGpsData() +{ + locationProvider->uploadToServer(); +} + void QMLManager::clearGpsData() { locationProvider->clearGpsData();