File indexing completed on 2024-12-08 07:19:12

0001 /*
0002     SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "vehiclelayoutreply.h"
0008 #include "reply_p.h"
0009 #include "vehiclelayoutrequest.h"
0010 #include "logging.h"
0011 #include "backends/abstractbackend.h"
0012 #include "backends/cache.h"
0013 
0014 #include <KPublicTransport/Platform>
0015 #include <KPublicTransport/Stopover>
0016 #include <KPublicTransport/Vehicle>
0017 
0018 #include <QDebug>
0019 
0020 using namespace KPublicTransport;
0021 
0022 namespace KPublicTransport {
0023 class VehicleLayoutReplyPrivate: public ReplyPrivate {
0024 public:
0025     void finalizeResult() override {}
0026 
0027     VehicleLayoutRequest request;
0028     Stopover stopover;
0029 };
0030 }
0031 
0032 VehicleLayoutReply::VehicleLayoutReply(const VehicleLayoutRequest &req, QObject *parent)
0033     : Reply(new VehicleLayoutReplyPrivate, parent)
0034 {
0035     Q_D(VehicleLayoutReply);
0036     d->request = req;
0037     d->stopover = req.stopover();
0038 }
0039 
0040 VehicleLayoutReply::~VehicleLayoutReply() = default;
0041 
0042 VehicleLayoutRequest VehicleLayoutReply::request() const
0043 {
0044     Q_D(const VehicleLayoutReply);
0045     return d->request;
0046 }
0047 
0048 Stopover VehicleLayoutReply::stopover() const
0049 {
0050     Q_D(const VehicleLayoutReply);
0051     return d->stopover;
0052 }
0053 
0054 static bool isOneSidedCar(VehicleSection::Type type)
0055 {
0056     return type == VehicleSection::PowerCar || type == VehicleSection::ControlCar;
0057 }
0058 
0059 void VehicleLayoutReply::addResult(const Stopover &stopover)
0060 {
0061     Q_D(VehicleLayoutReply);
0062     d->stopover = Stopover::merge(d->stopover, stopover);
0063 
0064     if (!d->stopover.vehicleLayout().sections().empty()) {
0065         // normalize section order
0066         auto vehicle = d->stopover.vehicleLayout();
0067         auto sections = vehicle.takeSections();
0068         std::sort(sections.begin(), sections.end(), [](const auto &lhs, const auto &rhs) {
0069             return lhs.platformPositionBegin() < rhs.platformPositionBegin();
0070         });
0071 
0072         // we have no connections at the ends
0073         sections.front().setConnectedSides(sections.front().connectedSides() & ~VehicleSection::Front);
0074         sections.back().setConnectedSides(sections.back().connectedSides() & ~VehicleSection::Back);
0075 
0076         // if the leading car in driving direction is a PassengerCar, turn it into a ControlCar
0077         if (vehicle.direction() == Vehicle::Forward && sections.front().type() == VehicleSection::PassengerCar) {
0078             sections.front().setType(VehicleSection::ControlCar);
0079         } else if (vehicle.direction() == Vehicle::Backward && sections.back().type() == VehicleSection::PassengerCar) {
0080             sections.back().setType(VehicleSection::ControlCar);
0081         }
0082 
0083         for (auto it = sections.begin(); it != sections.end(); ++it) {
0084             // engines and power cars have no connections either
0085             if ((*it).type() == VehicleSection::Engine) {
0086                 (*it).setConnectedSides(VehicleSection::NoSide);
0087             }
0088 
0089             if (it == sections.begin()) {
0090                 continue;
0091             }
0092 
0093             // connect control cars in the middle of the train to the correct side
0094             // only do that when two cars back isn't a control car either, ie. the preceeding car
0095             // actually has a connection to the front. Otherwise trains consisting of e.g. 4 consecutive
0096             // control cars get layouted wrongly.
0097             if (isOneSidedCar((*(it - 1)).type()) && isOneSidedCar((*it).type()) && ((*(it - 1)).connectedSides() & VehicleSection::Front)) {
0098                 (*it).setConnectedSides((*it).connectedSides() & ~VehicleSection::Front);
0099             }
0100 
0101             // make sure connections are symmetric
0102             if (((*(it - 1)).connectedSides() & VehicleSection::Back) == 0) {
0103                 (*it).setConnectedSides((*it).connectedSides() & ~VehicleSection::Front);
0104             }
0105             if (((*it).connectedSides() & VehicleSection::Front) == 0) {
0106                 (*(it - 1)).setConnectedSides((*(it - 1)).connectedSides() & ~VehicleSection::Back);
0107             }
0108         }
0109 
0110         vehicle.setSections(std::move(sections));
0111         d->stopover.setVehicleLayout(std::move(vehicle));
0112     }
0113 
0114     d->pendingOps--;
0115     d->emitFinishedIfDone(this);
0116 }
0117 
0118 void VehicleLayoutReply::addError(const AbstractBackend *backend, Reply::Error error, const QString &errorMsg)
0119 {
0120     if (error == Reply::NotFoundError) {
0121         // TODO add negative cache entry
0122     } else {
0123         qCDebug(Log) << backend->backendId() << error << errorMsg;
0124     }
0125     Reply::addError(error, errorMsg);
0126 }