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 <dirk@hohndel.org>
This commit is contained in:
Dirk Hohndel 2015-11-13 17:21:43 -08:00
parent 577da54454
commit 7afed04520
4 changed files with 85 additions and 3 deletions

View file

@ -4,8 +4,12 @@
#include "dive.h" #include "dive.h"
#include "helpers.h" #include "helpers.h"
#include <time.h> #include <time.h>
#include <unistd.h>
#include <QDebug> #include <QDebug>
#include <QVariant> #include <QVariant>
#include <QUrlQuery>
#include <QApplication>
#include <QTimer>
GpsLocation::GpsLocation(QObject *parent) GpsLocation::GpsLocation(QObject *parent)
{ {
@ -121,7 +125,7 @@ bool GpsLocation::applyLocations()
for (int j = last; j < cnt; j++) { for (int j = last; j < cnt; j++) {
if (time_during_dive_with_offset(d, gpsTable[j].when, SAME_GROUP)) { if (time_during_dive_with_offset(d, gpsTable[j].when, SAME_GROUP)) {
if (verbose) 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" << "which is withing six hours of dive from" <<
get_dive_date_string(d->when) << "until" << get_dive_date_string(d->when) << "until" <<
get_dive_date_string(d->when + d->duration.seconds); 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 (time_during_dive_with_offset(d, gpsTable[j].when, 0)) {
if (verbose) 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); copy_gps_location(gpsTable + j, d);
changed = true; changed = true;
last = j; last = j;
@ -167,7 +171,7 @@ bool GpsLocation::applyLocations()
last = j; last = j;
break; break;
} else { } 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 (d->when - gpsTable[j].when <= gpsTable[j+1].when - (d->when + d->duration.seconds)) {
if (verbose) if (verbose)
qDebug() << "pick the one before as it's closer to the start"; qDebug() << "pick the one before as it's closer to the start";
@ -215,3 +219,64 @@ void GpsLocation::clearGpsData()
geoSettings->clear(); geoSettings->clear();
geoSettings->sync(); 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);
}
}

View file

@ -7,6 +7,7 @@
#include <QGeoPositionInfoSource> #include <QGeoPositionInfoSource>
#include <QGeoPositionInfo> #include <QGeoPositionInfo>
#include <QSettings> #include <QSettings>
#include <QNetworkReply>
class GpsLocation : QObject class GpsLocation : QObject
{ {
@ -21,6 +22,7 @@ private:
QGeoPositionInfoSource *gpsSource; QGeoPositionInfoSource *gpsSource;
void status(QString msg); void status(QString msg);
QSettings *geoSettings; QSettings *geoSettings;
QNetworkReply *reply;
signals: signals:
@ -28,7 +30,10 @@ public slots:
void serviceEnable(bool toggle); void serviceEnable(bool toggle);
void newPosition(QGeoPositionInfo pos); void newPosition(QGeoPositionInfo pos);
void updateTimeout(); void updateTimeout();
void uploadToServer();
void postError(QNetworkReply::NetworkError error);
void clearGpsData(); void clearGpsData();
}; };
#endif // GPSLOCATION_H #endif // GPSLOCATION_H

View file

@ -86,6 +86,13 @@ ApplicationWindow {
} }
} }
MenuItem {
text: "Send GPS data to server"
onTriggered: {
manager.sendGpsData();
}
}
MenuItem { MenuItem {
text: "Clear stored GPS data" text: "Clear stored GPS data"
onTriggered: { onTriggered: {

View file

@ -158,6 +158,11 @@ void QMLManager::applyGpsData()
locationProvider->applyLocations(); locationProvider->applyLocations();
} }
void QMLManager::sendGpsData()
{
locationProvider->uploadToServer();
}
void QMLManager::clearGpsData() void QMLManager::clearGpsData()
{ {
locationProvider->clearGpsData(); locationProvider->clearGpsData();