diff --git a/src/publictransport/CMakeLists.txt b/src/publictransport/CMakeLists.txt index 47e81f3..c9623ed 100644 --- a/src/publictransport/CMakeLists.txt +++ b/src/publictransport/CMakeLists.txt @@ -1,64 +1,65 @@ set(kpublictransport_srcs departurereply.cpp departurerequest.cpp journeyreply.cpp journeyrequest.cpp manager.cpp reply.cpp backends/abstractbackend.cpp backends/hafasmgatebackend.cpp + backends/hafasmgateparser.cpp backends/navitiabackend.cpp backends/navitiaparser.cpp datatypes/departure.cpp datatypes/journey.cpp datatypes/line.cpp datatypes/location.cpp networks/networks.qrc ) ecm_qt_declare_logging_category(kpublictransport_srcs HEADER logging.h IDENTIFIER KPublicTransport::Log CATEGORY_NAME org.kde.kpublictransport) add_library(KPublicTransport STATIC ${kpublictransport_srcs}) target_include_directories(KPublicTransport PUBLIC "$") target_link_libraries(KPublicTransport PUBLIC Qt5::Gui PRIVATE Qt5::Network ) ecm_generate_headers(KPublicTransport_FORWARDING_HEADERS HEADER_NAMES DepartureReply DepartureRequest JourneyReply JourneyRequest Manager Reply PREFIX KPublicTransport REQUIRED_HEADERS KPublicTransport_HEADERS ) # # ### for testing only ecm_generate_headers(KPublicTransport_Backends_FORWARDING_HEADERS HEADER_NAMES NavitiaParser PREFIX KPublicTransport REQUIRED_HEADERS KPublicTransport_Backends_HEADERS RELATIVE backends ) ecm_generate_headers(KPublicTransport_Datatypes_FORWARDING_HEADERS HEADER_NAMES Datatypes Departure Journey Line Location PREFIX KPublicTransport REQUIRED_HEADERS KPublicTransport_Datatypes_HEADERS RELATIVE datatypes ) install(FILES org_kde_kpublictransport.categories DESTINATION ${KDE_INSTALL_CONFDIR}) diff --git a/src/publictransport/backends/hafasmgatebackend.cpp b/src/publictransport/backends/hafasmgatebackend.cpp index 330ba2a..8bea357 100644 --- a/src/publictransport/backends/hafasmgatebackend.cpp +++ b/src/publictransport/backends/hafasmgatebackend.cpp @@ -1,119 +1,130 @@ /* 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 "hafasmgatebackend.h" +#include "hafasmgateparser.h" +#include "logging.h" +#include #include #include #include #include #include #include #include #include #include #include #include using namespace KPublicTransport; HafasMgateBackend::HafasMgateBackend() = default; bool HafasMgateBackend::queryJourney(JourneyReply *reply, QNetworkAccessManager *nam) const { return false; } bool HafasMgateBackend::queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam) const { const auto request = reply->request(); const auto id = request.stop().identifier(QLatin1String("hafasId")); // ### temporary, until we have proper name lookup if (id.isEmpty()) { return false; } QJsonObject top; { QJsonObject auth; auth.insert(QLatin1String("aid"), m_aid); auth.insert(QLatin1String("type"), QLatin1String("AID")); top.insert(QLatin1String("auth"), auth); } { QJsonObject client; client.insert(QLatin1String("id"), m_clientId); client.insert(QLatin1String("type"), m_clientType); top.insert(QLatin1String("client"), client); } top.insert(QLatin1String("formatted"), false); top.insert(QLatin1String("lang"), QLatin1String("eng")); { QJsonArray svcReq; { QJsonObject req; req.insert(QLatin1String("getServerDateTime"), true); req.insert(QLatin1String("getTimeTablePeriod"), false); QJsonObject serverInfo; serverInfo.insert(QLatin1String("meth"), QLatin1String("ServerInfo")); serverInfo.insert(QLatin1String("req"), req); svcReq.push_back(serverInfo); } { QJsonObject cfg; cfg.insert(QLatin1String("polyEnc"), QLatin1String("GPA")); QJsonObject req; req.insert(QLatin1String("date"), request.dateTime().toString(QLatin1String("yyyyMMdd"))); req.insert(QLatin1String("maxJny"), 12); req.insert(QLatin1String("stbFltrEquiv"), true); QJsonObject stbLoc; stbLoc.insert(QLatin1String("extId"), id); stbLoc.insert(QLatin1String("state"), QLatin1String("F")); stbLoc.insert(QLatin1String("type"), QLatin1String("S")); req.insert(QLatin1String("stbLoc"), stbLoc); req.insert(QLatin1String("time"), request.dateTime().toString(QLatin1String("hhmmss"))); req.insert(QLatin1String("type"), QLatin1String("DEP")); QJsonObject stationBoard; stationBoard.insert(QLatin1String("cfg"), cfg); stationBoard.insert(QLatin1String("meth"), QLatin1String("StationBoard")); stationBoard.insert(QLatin1String("req"), req); svcReq.push_back(stationBoard); } top.insert(QLatin1String("svcReqL"), svcReq); } top.insert(QLatin1String("ver"), m_version); auto netReq = QNetworkRequest(QUrl(m_endpoint)); netReq.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json")); auto netReply = nam->post(netReq, QJsonDocument(top).toJson()); qDebug().noquote() << QJsonDocument(top).toJson(); QObject::connect(netReply, &QNetworkReply::finished, [netReply, reply]() { - qDebug() << netReply->errorString(); qDebug() << netReply->request().url(); - qDebug() << netReply->readAll(); + switch (netReply->error()) { + case QNetworkReply::NoError: + addResult(reply, HafasMgateParser::parseDepartures(netReply->readAll())); + break; + default: + addError(reply, Reply::NetworkError, netReply->errorString()); + qCDebug(Log) << netReply->error() << netReply->errorString(); + break; + } + netReply->deleteLater(); }); - return false; + return true; } diff --git a/src/publictransport/backends/hafasmgateparser.cpp b/src/publictransport/backends/hafasmgateparser.cpp new file mode 100644 index 0000000..dcec361 --- /dev/null +++ b/src/publictransport/backends/hafasmgateparser.cpp @@ -0,0 +1,138 @@ +/* + 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 "hafasmgateparser.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace KPublicTransport; + +static std::vector parseLocations(const QJsonArray &locL) +{ + std::vector locs; + locs.reserve(locL.size()); + for (const auto &locV : locL) { + const auto locObj = locV.toObject(); + Location loc; + loc.setName(locObj.value(QLatin1String("name")).toString()); + // TODO extId + const auto coordObj = locObj.value(QLatin1String("crd")).toObject(); + loc.setCoordinate(coordObj.value(QLatin1String("x")).toDouble() / 1000000.0, coordObj.value(QLatin1String("y")).toDouble() / 1000000.0); + locs.push_back(loc); + } + return locs; +} + +// ### mapping based on SNCB, is that the same everywhere? if not, this needs to move to the network config +static struct { + int productCode; + Line::Mode mode; +} const line_type_map[] { + { 4, Line::Train }, + { 64, Line::RapidTransit }, + { 256, Line::Metro }, + { 512, Line::Bus }, + { 1024, Line::Tramway } +}; + +static std::vector parseLines(const QJsonArray &prodL) +{ + std::vector lines; + lines.reserve(prodL.size()); + for (const auto &prodV : prodL) { + const auto prodObj = prodV.toObject(); + Line line; + line.setName(prodObj.value(QLatin1String("name")).toString()); + + const auto prodCode = prodObj.value(QLatin1String("cls")).toInt(); + for (auto it = std::begin(line_type_map); it != std::end(line_type_map); ++it) { + if ((*it).productCode == prodCode) { + line.setMode((*it).mode); + break; + } + } + lines.push_back(line); + } + + return lines; +} + +static std::vector parseStationBoardResponse(const QJsonObject &obj) +{ + const auto commonObj = obj.value(QLatin1String("common")).toObject(); + const auto locs = parseLocations(commonObj.value(QLatin1String("locL")).toArray()); + const auto lines = parseLines(commonObj.value(QLatin1String("prodL")).toArray()); + + std::vector res; + const auto jnyL = obj.value(QLatin1String("jnyL")).toArray(); + res.reserve(jnyL.size()); + + for (const auto &jny : jnyL) { + const auto jnyObj = jny.toObject(); + const auto stbStop = jnyObj.value(QLatin1String("stbStop")).toObject(); + + Route route; + route.setDirection(jnyObj.value(QLatin1String("dirTxt")).toString()); + const auto lineIdx = stbStop.value(QLatin1String("prodX")).toInt(); + if ((unsigned int)lineIdx < lines.size()) { + route.setLine(lines[lineIdx]); + } + + Departure dep; + dep.setRoute(route); + + const auto dateStr = jnyObj.value(QLatin1String("date")).toString(); + dep.setScheduledTime(QDateTime::fromString(dateStr + stbStop.value(QLatin1String("dTimeS")).toString(), QLatin1String("yyyyMMddhhmmss"))); + const auto dTimeP = stbStop.value(QLatin1String("dTimeP")).toString(); + if (!dTimeP.isEmpty()) { + dep.setActualTime(QDateTime::fromString(dateStr + dTimeP, QLatin1String("yyyyMMddhhmmss"))); + } + + // TODO platform + + const auto locIdx = stbStop.value(QLatin1String("locX")).toInt(); + if ((unsigned int)locIdx < locs.size()) { + dep.setStopPoint(locs[locIdx]); + } + + res.push_back(dep); + } + + return res; +} + +std::vector HafasMgateParser::parseDepartures(const QByteArray &data) +{ + const auto topObj = QJsonDocument::fromJson(data).object(); + const auto svcResL = topObj.value(QLatin1String("svcResL")).toArray(); + + for (const auto &v : svcResL) { + const auto obj = v.toObject(); + if (obj.value(QLatin1String("meth")).toString() == QLatin1String("StationBoard")) { + return parseStationBoardResponse(obj.value(QLatin1String("res")).toObject()); + } + } + + return {}; +} diff --git a/src/publictransport/backends/hafasmgateparser.h b/src/publictransport/backends/hafasmgateparser.h new file mode 100644 index 0000000..fd7010f --- /dev/null +++ b/src/publictransport/backends/hafasmgateparser.h @@ -0,0 +1,37 @@ +/* + 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_HAFASMGATEPARSER_H +#define KPUBLICTRANSPORT_HAFASMGATEPARSER_H + +#include + +class QByteArray; + +namespace KPublicTransport { + +class Departure; + +/** Hafas response parser. */ +namespace HafasMgateParser +{ + std::vector parseDepartures(const QByteArray &data); +} + +} + +#endif // KPUBLICTRANSPORT_HAFASMGATEPARSER_H