File indexing completed on 2024-05-12 04:42:33
0001 /* 0002 SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "deutschebahnbackend.h" 0008 #include "deutschebahnvehiclelayoutparser.h" 0009 #include "cache.h" 0010 0011 #include <KPublicTransport/Stopover> 0012 #include <KPublicTransport/VehicleLayoutReply> 0013 #include <KPublicTransport/VehicleLayoutRequest> 0014 0015 #include <QDebug> 0016 #include <QNetworkAccessManager> 0017 #include <QNetworkRequest> 0018 #include <QNetworkReply> 0019 #include <QRegularExpression> 0020 #include <QUrl> 0021 0022 using namespace KPublicTransport; 0023 0024 static QString extractTrainNumber(const Route &route) 0025 { 0026 if (!route.name().isEmpty()) { 0027 QRegularExpression regex(QStringLiteral("(?:[A-Z]+)?\\s*(\\d+)")); 0028 const auto match = regex.match(route.name()); 0029 if (match.hasMatch()) { 0030 return match.captured(1); 0031 } 0032 } 0033 0034 const auto line = route.line(); 0035 QRegularExpression regex(QStringLiteral("(?:ICE|IC|EC|RJ|NJ)\\s*(\\d+)")); 0036 const auto match = regex.match(line.modeString() + line.name()); 0037 if (match.hasMatch()) { 0038 return match.captured(1); 0039 } 0040 0041 return {}; 0042 } 0043 0044 bool DeutscheBahnBackend::queryVehicleLayout(const VehicleLayoutRequest &request, VehicleLayoutReply *reply, QNetworkAccessManager *nam) const 0045 { 0046 // unlike the rest of the DB API, this only works in Germany, so do our own geo filtering here. 0047 const auto germanyBBox = QPolygonF({ {5.56384, 55.0492}, {6.131, 47.2565}, {15.4307, 47.4737}, {14.6794, 54.7568} }); 0048 if (!germanyBBox.containsPoint({request.stopover().stopPoint().longitude(), request.stopover().stopPoint().latitude()}, Qt::WindingFill)) { 0049 qDebug() << "request outside of bounding box"; 0050 return false; 0051 } 0052 0053 // we need two parameters for the online API: the train number (numeric only), and the departure time 0054 // note: data is only available withing the upcoming 24h 0055 // checking this early is useful as the error response from the online service is extremely verbose... 0056 auto dt = request.stopover().scheduledDepartureTime().isValid() ? request.stopover().scheduledDepartureTime() : request.stopover().scheduledArrivalTime(); 0057 const auto trainNum = extractTrainNumber(request.stopover().route()); 0058 if (!dt.isValid() || trainNum.isEmpty()) { 0059 return false; 0060 } 0061 0062 // there are only valid results for a 24h time window, so try to adjust the date accordingly 0063 const auto now = QDateTime::currentDateTime(); 0064 if (dt.daysTo(now) > 1 || dt.daysTo(now) < -1) { 0065 qDebug() << "adjusting departure time to today:" << dt; 0066 dt.setDate(QDate::currentDate()); 0067 } 0068 0069 QUrl url; 0070 url.setScheme(QStringLiteral("https")); 0071 url.setHost(QStringLiteral("ist-wr.noncd.db.de")); 0072 url.setPath(QLatin1String("/wagenreihung/1.0/") + trainNum + QLatin1Char('/') + dt.toString(QStringLiteral("yyyyMMddhhmm"))); 0073 0074 QNetworkRequest netReq(url); 0075 logRequest(request, netReq); 0076 auto netReply = nam->get(netReq); 0077 netReply->setParent(reply); 0078 0079 QObject::connect(netReply, &QNetworkReply::finished, reply, [this, reply, netReply] { 0080 const auto data = netReply->readAll(); 0081 logReply(reply, netReply, data); 0082 0083 if (netReply->error() == QNetworkReply::NoError) { 0084 DeutscheBahnVehicleLayoutParser p; 0085 if (p.parse(data)) { 0086 Cache::addVehicleLayoutCacheEntry(backendId(), reply->request().cacheKey(), p.stopover, {}, std::chrono::minutes(2)); 0087 addResult(reply, p.stopover); 0088 } else { 0089 addError(reply, p.error, p.errorMessage); 0090 if (p.error == Reply::NotFoundError) { 0091 Cache::addNegativeVehicleLayoutCacheEntry(backendId(), reply->request().cacheKey(), std::chrono::hours(24)); 0092 } 0093 } 0094 } else { 0095 addError(reply, Reply::NetworkError, reply->errorString()); 0096 } 0097 netReply->deleteLater(); 0098 }); 0099 0100 return true; 0101 }