diff --git a/src/app/livedatamanager.cpp b/src/app/livedatamanager.cpp index 22e8847..fcfa68d 100644 --- a/src/app/livedatamanager.cpp +++ b/src/app/livedatamanager.cpp @@ -1,197 +1,197 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "livedatamanager.h" #include "logging.h" #include "pkpassmanager.h" #include "reservationmanager.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace KItinerary; LiveDataManager::LiveDataManager(QObject *parent) : QObject(parent) { } LiveDataManager::~LiveDataManager() = default; void LiveDataManager::setReservationManager(ReservationManager *resMgr) { m_resMgr = resMgr; // TODO set up change monitoring const auto resIds = resMgr->reservations(); for (const auto &resId : resIds) { const auto res = resMgr->reservation(resId); if (!LocationUtil::isLocationChange(res)) { // we only care about transit reservations continue; } if (SortUtil::endtDateTime(res) < QDateTime::currentDateTime().addDays(-1)) { continue; // we don't care about past events } m_reservations.push_back(resId); } std::sort(m_reservations.begin(), m_reservations.end(), [this](const auto &lhs, const auto &rhs) { return SortUtil::isBefore(m_resMgr->reservation(lhs), m_resMgr->reservation(rhs)); }); } void LiveDataManager::setPkPassManager(PkPassManager *pkPassMgr) { m_pkPassMgr = pkPassMgr; } QVariant LiveDataManager::arrival(const QString &resId) { return QVariant::fromValue(m_arrivals.value(resId)); } QVariant LiveDataManager::departure(const QString &resId) { return QVariant::fromValue(m_departures.value(resId)); } void LiveDataManager::checkForUpdates() { qCDebug(Log) << m_reservations.size(); m_pkPassMgr->updatePasses(); // TODO do this as part of the below loop for (auto it = m_reservations.begin(); it != m_reservations.end();) { const auto res = m_resMgr->reservation(*it); // clean up old stuff (TODO: do this a bit more precisely) if (SortUtil::endtDateTime(res) < QDateTime::currentDateTime().addDays(-1)) { it = m_reservations.erase(it); continue; } if (JsonLd::isA(res)) { checkTrainTrip(res.value().reservationFor().value(), *it); } // TODO check for pkpass updates ++it; } } static QString stripSpecial(const QString &str) { QString res; res.reserve(str.size()); std::copy_if(str.begin(), str.end(), std::back_inserter(res), [](const auto c) { return c.isLetter() || c.isDigit(); }); return res; } static bool isSameLine(const QString &lineName, const QString &trainName, const QString &trainNumber) { const auto lhs = stripSpecial(lineName); const auto rhs = stripSpecial(trainName + trainNumber); return lhs.compare(rhs, Qt::CaseInsensitive) == 0; } static KPublicTransport::Location locationFromStation(const TrainStation &station) { using namespace KPublicTransport; Location loc; loc.setName(station.name()); loc.setCoordinate(station.geo().latitude(), station.geo().longitude()); if (!station.identifier().isEmpty()) { const auto idSplit = station.identifier().split(QLatin1Char(':')); if (idSplit.size() == 2) { loc.setIdentifier(idSplit.at(0), idSplit.at(1)); } } return loc; } void LiveDataManager::checkTrainTrip(const TrainTrip& trip, const QString& resId) { qCDebug(Log) << trip.trainName() << trip.trainNumber() << trip.departureTime(); if (!m_ptMgr) { m_ptMgr.reset(new KPublicTransport::Manager); } using namespace KPublicTransport; DepartureRequest req(locationFromStation(trip.departureStation())); req.setDateTime(trip.departureTime()); auto reply = m_ptMgr->queryDeparture(req); connect(reply, &Reply::finished, this, [this, trip, resId, reply]() { reply->deleteLater(); if (reply->error() != Reply::NoError) { qCDebug(Log) << reply->error() << reply->errorString(); return; } - for (const auto &dep : reply->departures()) { + for (const auto &dep : reply->result()) { qCDebug(Log) << "Got departure information:" << dep.route().line().name() << dep.scheduledDepartureTime() << "for" << trip.trainNumber(); if (dep.scheduledDepartureTime() != trip.departureTime() || !isSameLine(dep.route().line().name(), trip.trainName(), trip.trainNumber())) { continue; } qCDebug(Log) << "Found departure information:" << dep.route().line().name() << dep.expectedPlatform() << dep.expectedDepartureTime(); m_departures.insert(resId, dep); emit departureUpdated(resId); break; } }); req = DepartureRequest(locationFromStation(trip.arrivalStation())); req.setMode(DepartureRequest::QueryArrival); req.setDateTime(trip.arrivalTime()); reply = m_ptMgr->queryDeparture(req); connect(reply, &Reply::finished, this, [this, trip, resId, reply]() { reply->deleteLater(); if (reply->error() != Reply::NoError) { qCDebug(Log) << reply->error() << reply->errorString(); return; } - for (const auto &arr : reply->departures()) { + for (const auto &arr : reply->result()) { qCDebug(Log) << "Got arrival information:" << arr.route().line().name() << arr.scheduledArrivalTime() << "for" << trip.trainNumber(); if (arr.scheduledArrivalTime() != trip.arrivalTime() || !isSameLine(arr.route().line().name(), trip.trainName(), trip.trainNumber())) { continue; } qCDebug(Log) << "Found arrival information:" << arr.route().line().name() << arr.expectedPlatform() << arr.expectedDepartureTime(); m_arrivals.insert(resId, arr); emit arrivalUpdated(resId); break; } }); } #include "moc_livedatamanager.cpp" diff --git a/src/publictransport/departurereply.cpp b/src/publictransport/departurereply.cpp index c756d8e..23d8882 100644 --- a/src/publictransport/departurereply.cpp +++ b/src/publictransport/departurereply.cpp @@ -1,111 +1,117 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "departurereply.h" #include "reply_p.h" #include "departurerequest.h" #include "logging.h" #include #include using namespace KPublicTransport; namespace KPublicTransport { class DepartureReplyPrivate : public ReplyPrivate { public: void finalizeResult() override; DepartureRequest request; - std::vector departures; + std::vector result; }; } void DepartureReplyPrivate::finalizeResult() { - if (departures.empty()) { + if (result.empty()) { return; } error = Reply::NoError; errorMsg.clear(); if (request.mode() == DepartureRequest::QueryDeparture) { - std::sort(departures.begin(), departures.end(), [](const auto &lhs, const auto &rhs) { + std::sort(result.begin(), result.end(), [](const auto &lhs, const auto &rhs) { return lhs.scheduledDepartureTime() < rhs.scheduledDepartureTime(); }); } else { - std::sort(departures.begin(), departures.end(), [](const auto &lhs, const auto &rhs) { + std::sort(result.begin(), result.end(), [](const auto &lhs, const auto &rhs) { return lhs.scheduledArrivalTime() < rhs.scheduledArrivalTime(); }); } - for (auto it = departures.begin(); it != departures.end(); ++it) { - for (auto mergeIt = it + 1; mergeIt != departures.end();) { + for (auto it = result.begin(); it != result.end(); ++it) { + for (auto mergeIt = it + 1; mergeIt != result.end();) { if (request.mode() == DepartureRequest::QueryDeparture) { if ((*it).scheduledDepartureTime() != (*mergeIt).scheduledDepartureTime()) { break; } } else { if ((*it).scheduledArrivalTime() != (*mergeIt).scheduledArrivalTime()) { break; } } if (Departure::isSame(*it, *mergeIt)) { *it = Departure::merge(*it, *mergeIt); - mergeIt = departures.erase(mergeIt); + mergeIt = result.erase(mergeIt); } else { ++mergeIt; } } } } DepartureReply::DepartureReply(const DepartureRequest &req) : Reply(new DepartureReplyPrivate) { Q_D(DepartureReply); d->request = req; } DepartureReply::~DepartureReply() = default; DepartureRequest DepartureReply::request() const { Q_D(const DepartureReply); return d->request; } -std::vector DepartureReply::departures() const +const std::vector& DepartureReply::result() const { Q_D(const DepartureReply); - return d->departures; // TODO this copies + return d->result; +} + +std::vector&& DepartureReply::takeResult() +{ + Q_D(DepartureReply); + return std::move(d->result); } void DepartureReply::addResult(std::vector &&res) { Q_D(DepartureReply); - if (d->departures.empty()) { - d->departures = std::move(res); + if (d->result.empty()) { + d->result = std::move(res); } else { - d->departures.insert(d->departures.end(), res.begin(), res.end()); + d->result.insert(d->result.end(), res.begin(), res.end()); } d->pendingOps--; d->emitFinishedIfDone(this); } diff --git a/src/publictransport/departurereply.h b/src/publictransport/departurereply.h index 03cc7b9..7ac9f11 100644 --- a/src/publictransport/departurereply.h +++ b/src/publictransport/departurereply.h @@ -1,57 +1,59 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KPUBLICTRANSPORT_DEPARTUREREPLY_H #define KPUBLICTRANSPORT_DEPARTUREREPLY_H #include "reply.h" #include namespace KPublicTransport { class AbstractBackend; class Departure; class DepartureRequest; class DepartureReplyPrivate; -/** Departure query reply. */ +/** Departure or arrival query reply. */ class DepartureReply : public Reply { Q_OBJECT public: ~DepartureReply(); /** The request this is the reply for. */ DepartureRequest request() const; - /** Returns the found departure information. */ - std::vector departures() const; + /** Returns the found arrival or departure information. */ + const std::vector& result() const; + /** Returns the found arrival or departure information for moving elsewhere. */ + std::vector&& takeResult(); private: friend class Manager; explicit DepartureReply(const DepartureRequest &req); friend class AbstractBackend; void addResult(std::vector &&res); Q_DECLARE_PRIVATE(DepartureReply) }; } #endif // KPUBLICTRANSPORT_DEPARTUREREPLY_H diff --git a/tests/departurequery.cpp b/tests/departurequery.cpp index fe7d724..7cf465c 100644 --- a/tests/departurequery.cpp +++ b/tests/departurequery.cpp @@ -1,124 +1,124 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace KPublicTransport; class QueryManager : public QObject { Q_OBJECT Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged) Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) public: QueryManager() { ptMgr.setNetworkAccessManager(&nam); } Q_INVOKABLE void queryDeparture(double fromLat, double fromLon, const QString &id, const QString &idType, bool queryArrival) { engine->rootContext()->setContextProperty(QStringLiteral("_departures"), QVariantList()); m_loading = true; emit loadingChanged(); m_errorMsg.clear(); emit errorMessageChanged(); Location from; from.setCoordinate(fromLat, fromLon); from.setIdentifier(idType, id); DepartureRequest depReq(from); depReq.setMode(queryArrival ? DepartureRequest::QueryArrival : DepartureRequest::QueryDeparture); auto reply = ptMgr.queryDeparture(depReq); QObject::connect(reply, &DepartureReply::finished, [reply, this]{ m_loading = false; emit loadingChanged(); if (reply->error() == DepartureReply::NoError) { - const auto res = reply->departures(); + const auto res = reply->takeResult(); QVariantList l; l.reserve(res.size()); std::transform(res.begin(), res.end(), std::back_inserter(l), [](const auto &journey) { return QVariant::fromValue(journey); }); engine->rootContext()->setContextProperty(QStringLiteral("_departures"), l); for (const auto &departure : res) { qDebug() << departure.stopPoint().name() << departure.route().line().name() << departure.route().direction() << departure.scheduledDepartureTime(); } } else { m_errorMsg = reply->errorString(); emit errorMessageChanged(); } }); } Q_INVOKABLE void setAllowInsecure(bool insecure) { ptMgr.setAllowInsecureBackends(insecure); } bool loading() const { return m_loading; } QString errorMessage() const { return m_errorMsg; } QQmlEngine *engine = nullptr; signals: void loadingChanged(); void errorMessageChanged(); private: QNetworkAccessManager nam; Manager ptMgr; QString m_errorMsg; bool m_loading = false; }; int main(int argc, char **argv) { QCoreApplication::setApplicationName(QStringLiteral("departurequery")); QCoreApplication::setOrganizationName(QStringLiteral("KDE")); QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org")); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QApplication app(argc, argv); qmlRegisterUncreatableType("org.kde.kpublictransport", 1, 0, "Line", {}); QueryManager mgr; QQmlApplicationEngine engine; mgr.engine = &engine; engine.rootContext()->setContextProperty(QStringLiteral("_queryMgr"), &mgr); engine.load(QStringLiteral("qrc:/departurequery.qml")); return app.exec(); } #include "departurequery.moc"