LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_FCDReplay.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 91.4 % 255 233
Test Date: 2026-09-05 15:36:08 Functions: 94.1 % 17 16

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2013-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    MSDevice_FCDReplay.cpp
      15              : /// @author  Michael Behrisch
      16              : /// @date    01.03.2024
      17              : ///
      18              : // A device which replays recorded floating car data
      19              : /****************************************************************************/
      20              : #include <config.h>
      21              : 
      22              : #include <utils/common/StaticCommand.h>
      23              : #include <utils/geom/Position.h>
      24              : #include <utils/options/OptionsCont.h>
      25              : #include <utils/xml/SUMOSAXReader.h>
      26              : #include <utils/xml/XMLSubSys.h>
      27              : #include <libsumo/Vehicle.h>
      28              : #include <microsim/MSNet.h>
      29              : #include <microsim/MSEdge.h>
      30              : #include <microsim/MSLane.h>
      31              : #include <microsim/MSRoute.h>
      32              : #include <microsim/MSEventControl.h>
      33              : #include <microsim/MSInsertionControl.h>
      34              : #include <microsim/transportables/MSTransportableControl.h>
      35              : #include <microsim/transportables/MSStageDriving.h>
      36              : #include <microsim/transportables/MSStageWaiting.h>
      37              : #include <microsim/transportables/MSStageWalking.h>
      38              : #include "MSTransportableDevice_FCDReplay.h"
      39              : #include "MSDevice_FCDReplay.h"
      40              : 
      41              : 
      42              : // ===========================================================================
      43              : // static member initializations
      44              : // ===========================================================================
      45              : std::vector<std::unique_ptr<MSDevice_FCDReplay::FCDHandler> > MSDevice_FCDReplay::myHandlers;
      46              : std::vector<std::unique_ptr<SUMOSAXReader> > MSDevice_FCDReplay::myParsers;
      47              : SumoXMLAttrMask MSDevice_FCDReplay::myUsedAttributes;
      48              : 
      49              : 
      50              : // ===========================================================================
      51              : // method definitions
      52              : // ===========================================================================
      53              : // ---------------------------------------------------------------------------
      54              : // static initialisation methods
      55              : // ---------------------------------------------------------------------------
      56              : void
      57        45763 : MSDevice_FCDReplay::insertOptions(OptionsCont& oc) {
      58        45763 :     oc.addOptionSubTopic("FCD Replay Device");
      59        91526 :     insertDefaultAssignmentOptions("fcd-replay", "FCD Replay Device", oc);
      60              : 
      61        45763 :     oc.doRegister("device.fcd-replay.files", new Option_FileName());
      62        91526 :     oc.addSynonyme("device.fcd-replay.files", "device.fcd-replay.file");
      63        91526 :     oc.addDescription("device.fcd-replay.files", "FCD Replay Device", TL("FCD files to read"));
      64       183052 :     oc.doRegister("device.fcd-replay.consistency", new Option_StringVector(StringVector({ "all" })));
      65        91526 :     oc.addDescription("device.fcd-replay.consistency", "FCD Replay Device", TL("Values to use when replaying ('all' or any combination of 'x', 'y', 'edge', 'lane', 'speed', 'position', 'angle', 'type')"));
      66       137289 : }
      67              : 
      68              : 
      69              : void
      70      5440076 : MSDevice_FCDReplay::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
      71      5440076 :     OptionsCont& oc = OptionsCont::getOptions();
      72     10880152 :     if (equippedByDefaultAssignmentOptions(oc, "fcd-replay", v, oc.isSet("device.fcd-replay.files"))) {
      73           44 :         MSDevice_FCDReplay* device = new MSDevice_FCDReplay(v, "fcdReplay_" + v.getID());
      74           44 :         into.push_back(device);
      75              :     }
      76      5440076 : }
      77              : 
      78              : 
      79              : void
      80        41631 : MSDevice_FCDReplay::init() {
      81        41631 :     const OptionsCont& oc = OptionsCont::getOptions();
      82              :     myUsedAttributes.reset();
      83       124893 :     for (const std::string& attr : oc.getStringVector("device.fcd-replay.consistency")) {
      84        41631 :         if (attr == "x" || attr == "all") {
      85              :             myUsedAttributes.set(SUMO_ATTR_X);
      86              :         }
      87        41631 :         if (attr == "y" || attr == "all") {
      88              :             myUsedAttributes.set(SUMO_ATTR_Y);
      89              :         }
      90        41631 :         if (attr == "edge" || attr == "all") {
      91              :             myUsedAttributes.set(SUMO_ATTR_EDGE);
      92              :         }
      93        41631 :         if (attr == "lane" || attr == "all") {
      94              :             myUsedAttributes.set(SUMO_ATTR_LANE);
      95              :         }
      96        41631 :         if (attr == "speed" || attr == "all") {
      97              :             myUsedAttributes.set(SUMO_ATTR_SPEED);
      98              :         }
      99        41631 :         if (attr == "position" || attr == "pos" || attr == "all") {
     100              :             myUsedAttributes.set(SUMO_ATTR_POSITION);
     101              :         }
     102        41631 :         if (attr == "angle" || attr == "all") {
     103              :             myUsedAttributes.set(SUMO_ATTR_ANGLE);
     104              :         }
     105        41631 :         if (attr == "type" || attr == "all") {
     106              :             myUsedAttributes.set(SUMO_ATTR_TYPE);
     107              :         }
     108              :     }
     109              :     myHandlers.clear();
     110        83262 :     if (oc.isSet("device.fcd-replay.files")) {
     111           84 :         for (const std::string& filename : oc.getStringVector("device.fcd-replay.files")) {
     112           56 :             myHandlers.push_back(std::unique_ptr<MSDevice_FCDReplay::FCDHandler>(new MSDevice_FCDReplay::FCDHandler(filename)));
     113           56 :             myParsers.push_back(std::unique_ptr<SUMOSAXReader>(XMLSubSys::getSAXReader(*myHandlers.back())));
     114           56 :             if (!myParsers.back()->parseFirst(filename)) {
     115            0 :                 throw ProcessError(TLF("Can not read XML-file '%'.", filename));
     116              :             }
     117           28 :             const SUMOTime inc = parseNext(SIMSTEP);
     118           28 :             MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(new MoveVehicles(), SIMSTEP + DELTA_T);
     119           28 :             if (inc > 0) {
     120           56 :                 MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(new StaticCommand<MSDevice_FCDReplay>(&MSDevice_FCDReplay::parseNext),
     121           28 :                         SIMSTEP + inc);
     122              :             }
     123              :         }
     124              :     }
     125        41631 : }
     126              : 
     127              : 
     128              : SUMOTime
     129           48 : MSDevice_FCDReplay::parseNext(SUMOTime t) {
     130           48 :     SUMOTime inc = string2time(OptionsCont::getOptions().getString("route-steps"));
     131              :     bool haveData = false;
     132           96 :     for (int i = 0; i < (int)myHandlers.size(); i++) {
     133           48 :         if (myParsers[i] != nullptr) {
     134              :             haveData = true;
     135              :             // make sure that we have always at least inc time steps buffered, so at time 200 we will parse 400 to 600
     136              :             const SUMOTime start = myHandlers[i]->getTime();
     137        60388 :             while (myHandlers[i]->getTime() < t + 2 * inc) {
     138        60384 :                 if (!myParsers[i]->parseNext()) {
     139              :                     myParsers[i] = nullptr;
     140              :                     break;
     141              :                 }
     142              :             }
     143           32 :             myHandlers[i]->updateTrafficObjects(start);
     144              :         }
     145              :     }
     146           64 :     return haveData ? inc : 0;
     147              : }
     148              : 
     149              : 
     150              : // ---------------------------------------------------------------------------
     151              : // MSDevice_FCDReplay-methods
     152              : // ---------------------------------------------------------------------------
     153           44 : MSDevice_FCDReplay::MSDevice_FCDReplay(SUMOVehicle& holder, const std::string& id) :
     154           44 :     MSVehicleDevice(holder, id) {
     155           44 : }
     156              : 
     157              : 
     158           88 : MSDevice_FCDReplay::~MSDevice_FCDReplay() {
     159           88 : }
     160              : 
     161              : 
     162              : void
     163         3872 : MSDevice_FCDReplay::move(SUMOTime currentTime) {
     164         3872 :     if (myTrajectory == nullptr) {
     165            8 :         for (auto& handler : myHandlers) {
     166            8 :             auto it = handler->getTrajectories().find(myHolder.getID());
     167            8 :             if (it != handler->getTrajectories().end()) {
     168            8 :                 setTrajectory(&it->second);
     169              :                 break;
     170              :             }
     171              :         }
     172            8 :         if (myTrajectory == nullptr) {
     173              :             return;
     174              :         }
     175         3864 :     } else if (myTrajectoryIndex == (int)myTrajectory->size()) {
     176              :         // removal happens via the usual MSVehicle::hasArrived mechanism
     177              :         // TODO we may need to set an arrivalPos
     178              :         return;
     179              :     }
     180         3832 :     MSVehicle* v = dynamic_cast<MSVehicle*>(&myHolder);
     181         3832 :     const TrajectoryEntry& te = myTrajectory->at(myTrajectoryIndex);
     182         3832 :     if (v == nullptr || te.time > currentTime) {
     183              :         return;
     184              :     }
     185         3820 :     if (te.edgeOrLane != "") {
     186         5400 :         const std::string& edgeID = SUMOXMLDefinitions::getEdgeIDFromLane(te.edgeOrLane);
     187         2700 :         const int laneIdx = SUMOXMLDefinitions::getIndexFromLane(te.edgeOrLane);
     188         2700 :         libsumo::Vehicle::moveToXY(myHolder.getID(), edgeID, laneIdx, te.pos.x(), te.pos.y(), te.angle, 7);
     189         1120 :     } else if (te.pos.x() != libsumo::INVALID_DOUBLE_VALUE) {
     190         1120 :         libsumo::Vehicle::moveToXY(myHolder.getID(), "", -1, te.pos.x(), te.pos.y(), te.angle, 0);
     191              :     }
     192         3820 :     v->getInfluencer().setSpeedMode(0);
     193         3820 :     libsumo::Vehicle::setSpeed(myHolder.getID(), te.speed);
     194              :     // libsumo::Vehicle::changeLane(myHolder.getID(), laneIdx, TS);
     195         3820 :     myTrajectoryIndex++;
     196              : }
     197              : 
     198              : 
     199              : SUMOTime
     200         4856 : MSDevice_FCDReplay::MoveVehicles::execute(SUMOTime currentTime) {
     201         4856 :     MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
     202        11728 :     for (MSVehicleControl::constVehIt i = c.loadedVehBegin(); i != c.loadedVehEnd(); ++i) {
     203         6872 :         MSDevice_FCDReplay* device = static_cast<MSDevice_FCDReplay*>(i->second->getDevice(typeid(MSDevice_FCDReplay)));
     204         6872 :         if (device != nullptr && i->second->hasDeparted()) {
     205         3872 :             device->move(currentTime);
     206              :         }
     207              :     }
     208         4856 :     return DELTA_T;
     209              : }
     210              : 
     211              : 
     212              : // ---------------------------------------------------------------------------
     213              : // MSDevice_FCDReplay::FCDHandler-methods
     214              : // ---------------------------------------------------------------------------
     215           28 : MSDevice_FCDReplay::FCDHandler::FCDHandler(const std::string& file) :
     216              :     SUMOSAXHandler(file),
     217              :     MapMatcher(false, false,
     218           28 :                OptionsCont::getOptions().getFloat("mapmatch.distance"),
     219          112 :                MsgHandler::getErrorInstance()) {}
     220              : 
     221              : 
     222              : void
     223        26164 : MSDevice_FCDReplay::FCDHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
     224        26164 :     bool ok = true;
     225        26164 :     switch (element) {
     226         4952 :         case SUMO_TAG_TIMESTEP:
     227         4952 :             myTime = attrs.getSUMOTimeReporting(SUMO_ATTR_TIME, "", ok);
     228              :             myPositions.clear();
     229        26136 :             return;
     230        21184 :         case SUMO_TAG_VEHICLE:
     231              :         case SUMO_TAG_PERSON:
     232        21184 :             if (myTime >= SIMSTEP) {
     233        21184 :                 const bool isPerson = element == SUMO_TAG_PERSON;
     234        21184 :                 const std::string id = attrs.getString(SUMO_ATTR_ID);
     235       105920 :                 auto getOptDouble = [&](const SumoXMLAttr attr) {
     236       105920 :                     return myUsedAttributes.test(attr) ? attrs.getOpt<double>(attr, id.c_str(), ok, libsumo::INVALID_DOUBLE_VALUE) : libsumo::INVALID_DOUBLE_VALUE;
     237        21184 :                 };
     238        21184 :                 const Position xy = Position(getOptDouble(SUMO_ATTR_X), getOptDouble(SUMO_ATTR_Y));
     239        42368 :                 const std::string type = myUsedAttributes.test(SUMO_ATTR_TYPE) ? attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "") : "";
     240              :                 std::string edgeOrLane;
     241        21184 :                 if (myUsedAttributes.test(SUMO_ATTR_EDGE) || myUsedAttributes.test(SUMO_ATTR_LANE)) {
     242        46308 :                     edgeOrLane = attrs.getOpt<std::string>(isPerson ? SUMO_ATTR_EDGE : SUMO_ATTR_LANE, id.c_str(), ok, "");
     243              :                 }
     244        21184 :                 const double speed = getOptDouble(SUMO_ATTR_SPEED);
     245        21184 :                 const double pos = getOptDouble(SUMO_ATTR_POSITION);
     246        21184 :                 const double angle = getOptDouble(SUMO_ATTR_ANGLE);
     247        21184 :                 std::string vehicle = attrs.getOpt<std::string>(SUMO_ATTR_VEHICLE, id.c_str(), ok, "");
     248        21184 :                 if (isPerson) {
     249        17244 :                     if (vehicle == "") {
     250              :                         const auto& veh = myPositions.find(xy);
     251        17244 :                         if (veh != myPositions.end()) {
     252         2788 :                             vehicle = veh->second;
     253              :                         }
     254              :                     }
     255              :                 } else {
     256         3940 :                     myPositions[xy] = id;
     257              :                 }
     258        21184 :                 if (!ok) {
     259            0 :                     WRITE_WARNING("Invalid  FCD data.");
     260              :                     return;
     261              :                 }
     262        21184 :                 myTrajectories[id].push_back({myTime, xy, edgeOrLane, pos, speed, angle});
     263        42368 :                 const MSEdge* edge = MSEdge::dictionary(isPerson ? edgeOrLane : SUMOXMLDefinitions::getEdgeIDFromLane(edgeOrLane));
     264        21184 :                 if (edge == nullptr && edgeOrLane != "") {
     265            0 :                     WRITE_WARNINGF(isPerson ? TL("Unknown edge '%' in fcd replay file for person '%'.") : TL("Unknown lane '%' in fcd replay file for vehicle '%'."), edgeOrLane, id);
     266              :                 }
     267              :                 if (myRoutes.count(id) == 0) {
     268          208 :                     if (edgeOrLane == "") {
     269           16 :                         const MSLane* const lane = getClosestLane(xy, SVC_PASSENGER);
     270           16 :                         if (lane != nullptr) {
     271            8 :                             myTrajectories[id].back().edgeOrLane = lane->getID();
     272            8 :                             myTrajectories[id].back().lanePos = lane->interpolateGeometryPosToLanePos(
     273              :                                                                     lane->getShape().nearest_offset_to_point25D(xy, false));
     274            8 :                             edge = &lane->getEdge();
     275            8 :                             while (edge->isInternal()) {  // we cannot use an internal edge for a route
     276            0 :                                 edge = edge->getSuccessors().front();
     277              :                             }
     278              :                         }
     279              :                     }
     280          624 :                     myRoutes[id] = std::make_tuple(myTime, type, isPerson, ConstMSEdgeVector{edge}, std::vector<StageStart>());
     281              :                 } else {
     282        20976 :                     ConstMSEdgeVector& route = std::get<3>(myRoutes[id]);
     283        20976 :                     if (edge != nullptr && !edge->isInternal() && edge != route.back()) {
     284         1288 :                         route.push_back(edge);
     285              :                     }
     286              :                 }
     287        21184 :                 std::vector<StageStart>& vehicleUsage = std::get<4>(myRoutes[id]);
     288        21184 :                 if ((vehicleUsage.empty() && vehicle != "") || (!vehicleUsage.empty() && vehicle != vehicleUsage.back().vehicle)) {
     289          160 :                     vehicleUsage.push_back({vehicle, (int)myTrajectories[id].size() - 1, (int)std::get<3>(myRoutes[id]).size() - 1});
     290              :                 }
     291              :             }
     292              :             return;
     293              :         default:
     294              :             break;
     295              :     }
     296        42448 : }
     297              : 
     298              : 
     299              : MSTransportable::MSTransportablePlan*
     300          188 : MSDevice_FCDReplay::FCDHandler::makePlan(const SUMOVehicleParameter& params, const ConstMSEdgeVector& route,
     301              :         const std::vector<StageStart>& stages, const Trajectory& t) {
     302          188 :     MSTransportable::MSTransportablePlan* plan = new MSTransportable::MSTransportablePlan();
     303          376 :     plan->push_back(new MSStageWaiting(route.front(), nullptr, 0, params.depart, params.departPos, "awaiting departure", true));
     304              :     int prevRouteOffset = 0;
     305          188 :     const MSEdge* start = route.front();
     306              :     std::string prevVeh;
     307          316 :     for (const auto& stageStart : stages) {
     308          128 :         if (stageStart.vehicle != prevVeh) {
     309          128 :             if (stageStart.trajectoryOffset != 0) {
     310          128 :                 const MSEdge* prevEdge = MSEdge::dictionary(t[stageStart.trajectoryOffset - 1].edgeOrLane);
     311          128 :                 if (prevVeh == "") {
     312              :                     MSStoppingPlace* finalStop = nullptr;
     313           64 :                     if (prevRouteOffset < (int)route.size() - 1 && (route[prevRouteOffset]->getPermissions() & SVC_PEDESTRIAN) == 0) {
     314            0 :                         prevRouteOffset++;  // skip the access
     315              :                     }
     316           64 :                     int offset = stageStart.routeOffset;
     317           64 :                     if (offset < (int)route.size() - 1 && (route[offset]->getPermissions() & SVC_PEDESTRIAN) == SVC_PEDESTRIAN) {
     318            0 :                         offset++;  // a bus stop or two consecutive walks so include the edge in both
     319              :                     } else {
     320              :                         // may have an access, let's find the stop
     321           64 :                         const std::string& stop = MSNet::getInstance()->getStoppingPlaceID(route[offset]->getLanes()[0], t[stageStart.trajectoryOffset].lanePos, SUMO_TAG_BUS_STOP);
     322           64 :                         if (stop != "") {
     323           64 :                             finalStop = MSNet::getInstance()->getStoppingPlace(stop, SUMO_TAG_BUS_STOP);
     324              :                         }
     325              :                     }
     326           64 :                     ConstMSEdgeVector subRoute = ConstMSEdgeVector(route.begin() + prevRouteOffset, route.begin() + offset);
     327           64 :                     plan->push_back(new MSStageWalking(params.id, subRoute, finalStop, -1, params.departSpeed, params.departPos, 0, 0));
     328           64 :                 } else {
     329          192 :                     plan->push_back(new MSStageDriving(start, prevEdge, nullptr, -1, 0, {prevVeh}));
     330              :                 }
     331          128 :                 start = MSEdge::dictionary(t[stageStart.trajectoryOffset].edgeOrLane);
     332              :                 prevVeh = stageStart.vehicle;
     333          128 :                 prevRouteOffset = stageStart.routeOffset;
     334              :             }
     335              :         }
     336              :     }
     337              :     // final stage
     338          188 :     if (prevVeh == "") {
     339          188 :         if (prevRouteOffset < (int)route.size() - 1 && (route[prevRouteOffset]->getPermissions() & SVC_PEDESTRIAN) == 0) {
     340           64 :             prevRouteOffset++;
     341              :         }
     342              :         int offset = (int)route.size() - 1;
     343          188 :         if ((route[offset]->getPermissions() & SVC_PEDESTRIAN) == SVC_PEDESTRIAN) {
     344              :             offset++;
     345              :         }
     346          188 :         if (prevRouteOffset < offset) {
     347              :             // otherwise we may be still in a vehicle or in access, skip the stage for now
     348          188 :             ConstMSEdgeVector subRoute = ConstMSEdgeVector(route.begin() + prevRouteOffset, route.begin() + offset);
     349          188 :             plan->push_back(new MSStageWalking(params.id, subRoute, nullptr, -1, params.departSpeed, params.departPos, 0, 0));
     350          188 :         }
     351              :     } else {
     352            0 :         plan->push_back(new MSStageDriving(start, route.back(), nullptr, -1, 0, {prevVeh}));
     353              :     }
     354          188 :     return plan;
     355              : }
     356              : 
     357              : 
     358              : ConstMSEdgeVector
     359           40 : MSDevice_FCDReplay::FCDHandler::checkRoute(const ConstMSEdgeVector& edges, const SUMOVehicle* const vehicle) {
     360              :     ConstMSEdgeVector checkedRoute;
     361          480 :     for (const MSEdge* const e : edges) {
     362          440 :         if (checkedRoute.empty() || checkedRoute.back()->isConnectedTo(*e, vehicle->getVehicleType().getVehicleClass())) {
     363          432 :             checkedRoute.push_back(e);
     364              :         } else {
     365            8 :             const MSEdge* fromEdge = checkedRoute.back();
     366              :             checkedRoute.pop_back();
     367           16 :             if (!MSNet::getInstance()->getRouterTT(0).compute(fromEdge, e, vehicle, myTime, checkedRoute)) {
     368              :                 // TODO maybe warn about disconnected route
     369            4 :                 checkedRoute.push_back(fromEdge);
     370            4 :                 checkedRoute.push_back(e);
     371              :             }
     372              :             // TODO check whether we introduced a big detour
     373              :         }
     374              :     }
     375           40 :     return checkedRoute;
     376            0 : }
     377              : 
     378              : 
     379              : void
     380           32 : MSDevice_FCDReplay::FCDHandler::updateTrafficObjects(const SUMOTime intervalStart) {
     381          292 :     for (const auto& desc : myRoutes) {
     382          260 :         const std::string& id = desc.first;
     383          260 :         const bool isPerson = std::get<2>(desc.second);
     384              :         const ConstMSEdgeVector& routeEdges = std::get<3>(desc.second);
     385          260 :         Trajectory& t = myTrajectories[id];
     386          260 :         if (t.front().time >= intervalStart) {
     387              :             // new vehicle or person
     388          208 :             SUMOVehicleParameter* params = new SUMOVehicleParameter();
     389          208 :             params->id = id;
     390          208 :             params->depart = std::get<0>(desc.second);
     391          208 :             params->departPos = t.front().lanePos;
     392          208 :             params->departSpeed = t.front().speed;
     393              :             // params->arrivalPos = t.back().lanePos;
     394              :             std::string vType = std::get<1>(desc.second);
     395          208 :             if (vType == "") {
     396           56 :                 vType = isPerson ? DEFAULT_PEDTYPE_ID : DEFAULT_VTYPE_ID;
     397              :             }
     398          208 :             MSVehicleType* vehicleType = MSNet::getInstance()->getVehicleControl().getVType(vType);
     399          208 :             if (vehicleType == nullptr) {
     400            0 :                 delete params;
     401            0 :                 throw ProcessError(TLF("Unknown vType '%'.", vType));
     402              :             }
     403          208 :             if (routeEdges.front() == nullptr) {
     404            8 :                 if (isPerson) {
     405            0 :                     WRITE_WARNINGF(TL("No edge in fcd replay file for person '%' at time %."), id, time2string(t.front().time));
     406              :                 } else {
     407           24 :                     WRITE_WARNINGF(TL("No lane in fcd replay file for vehicle '%' at time %."), id, time2string(t.front().time));
     408              :                 }
     409            8 :                 delete params;
     410            8 :                 continue;
     411              :             }
     412          200 :             if (isPerson) {
     413          164 :                 MSTransportable::MSTransportablePlan* plan = makePlan(*params, routeEdges, std::get<4>(desc.second), t);
     414              :                 // plan completed, now build the person
     415          164 :                 MSTransportable* person = MSNet::getInstance()->getPersonControl().buildPerson(params, vehicleType, plan, nullptr);
     416          164 :                 person->getSingularType().setVClass(SVC_IGNORING);
     417          164 :                 if (!MSNet::getInstance()->getPersonControl().add(person)) {
     418            0 :                     delete person;
     419            0 :                     throw ProcessError(TLF("Duplicate person '%'.", id));
     420              :                 }
     421          164 :                 MSTransportableDevice_FCDReplay* device = static_cast<MSTransportableDevice_FCDReplay*>(person->getDevice(typeid(MSTransportableDevice_FCDReplay)));
     422          164 :                 if (device == nullptr) {  // Person did not get a replay device
     423            0 :                     delete person;
     424            0 :                     continue;
     425              :                 }
     426              :                 device->setTrajectory(&t);
     427              :             } else {
     428           36 :                 const std::string dummyRouteID = "DUMMY_ROUTE_" + id;
     429              :                 const StopParVector stops;
     430           36 :                 ConstMSRoutePtr route = std::make_shared<MSRoute>(dummyRouteID, routeEdges, true, nullptr, stops);
     431           72 :                 if (!MSRoute::dictionary(dummyRouteID, route)) {
     432            0 :                     throw ProcessError(TLF("Could not add route '%'.", dummyRouteID));
     433              :                 }
     434           36 :                 if (t.front().edgeOrLane != "") {
     435           36 :                     params->departPosProcedure = DepartPosDefinition::GIVEN;
     436           36 :                     params->departLaneProcedure = DepartLaneDefinition::GIVEN;
     437           36 :                     params->departLane = SUMOXMLDefinitions::getIndexFromLane(t.front().edgeOrLane);
     438              :                 }
     439           36 :                 SUMOVehicle* vehicle = MSNet::getInstance()->getVehicleControl().buildVehicle(params, route, vehicleType, false, MSVehicleControl::VehicleDefinitionSource::OTHER);
     440           36 :                 if (!MSNet::getInstance()->getVehicleControl().addVehicle(id, vehicle)) {
     441            0 :                     throw ProcessError(TLF("Duplicate vehicle '%'.", id));
     442              :                 }
     443           36 :                 MSNet::getInstance()->getInsertionControl().add(vehicle);
     444           36 :                 MSDevice_FCDReplay* device = static_cast<MSDevice_FCDReplay*>(vehicle->getDevice(typeid(MSDevice_FCDReplay)));
     445            0 :                 if (device == nullptr) {  // Vehicle did not get a replay device
     446            0 :                     MSNet::getInstance()->getVehicleControl().deleteVehicle(vehicle, true);
     447              :                     continue;
     448              :                 }
     449              :                 device->setTrajectory(&t);
     450              : 
     451              :                 // repair the route, cannot do this on parsing because a vehicle is needed
     452           36 :                 ConstMSEdgeVector checkedRoute = checkRoute(routeEdges, vehicle);
     453           36 :                 if (checkedRoute.size() != routeEdges.size()) {
     454            8 :                     vehicle->replaceRouteEdges(checkedRoute, -1, 0, "FCDReplay", true);
     455              :                 }
     456           72 :             }
     457           52 :         } else if (t.back().time >= intervalStart) {
     458              :             // new data for existing person / vehicle
     459           28 :             if (isPerson) {
     460           24 :                 MSTransportable* person = MSNet::getInstance()->getPersonControl().get(id);
     461           24 :                 if (person == nullptr) {
     462              :                     // TODO this should not happen
     463            0 :                     continue;
     464              :                 }
     465              :                 // TODO optimize: no need to regenerate the whole plan
     466           24 :                 MSTransportable::MSTransportablePlan* plan = makePlan(person->getParameter(), routeEdges, std::get<4>(desc.second), t);
     467           24 :                 const int stageIndex = person->getNumRemainingStages() - 1;
     468              :                 const MSStage* const finalStage = person->getNextStage(stageIndex);
     469              :                 bool append = false;
     470          120 :                 for (MSStage* const stage : *plan) {
     471           96 :                     if (stage->getStageType() == finalStage->getStageType() && stage->getFromEdge() == finalStage->getFromEdge()) {
     472              :                         // TODO: circular plans?
     473              :                         append = true;
     474              :                     }
     475           72 :                     if (append) {
     476           24 :                         person->appendStage(stage);
     477              :                     } else {
     478           72 :                         delete stage;
     479              :                     }
     480              :                 }
     481           24 :                 person->removeStage(stageIndex);
     482           24 :                 delete plan;
     483              :             } else {
     484            4 :                 SUMOVehicle* vehicle = MSNet::getInstance()->getVehicleControl().getVehicle(id);
     485            4 :                 ConstMSEdgeVector checkedRoute = checkRoute(routeEdges, vehicle);
     486            4 :                 if ((int)checkedRoute.size() != vehicle->getRoute().size()) {
     487            8 :                     vehicle->replaceRouteEdges(checkedRoute, -1, 0, "FCDReplay", true);
     488              :                 }
     489            4 :             }
     490              :         }
     491              :     }
     492           32 : }
     493              : 
     494              : 
     495              : void
     496            8 : MSDevice_FCDReplay::FCDHandler::initLaneTree(NamedRTree* tree) {
     497          128 :     for (const auto& edge : MSEdge::getAllEdges()) {
     498          312 :         for (MSLane* lane : edge->getLanes()) {
     499          192 :             Boundary b = lane->getShape().getBoxBoundary();
     500          192 :             const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
     501          192 :             const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
     502          192 :             tree->Insert(cmin, cmax, lane);
     503              :         }
     504              :     }
     505            8 : }
     506              : 
     507              : 
     508              : MSEdge*
     509            0 : MSDevice_FCDReplay::FCDHandler::retrieveEdge(const std::string& id) {
     510            0 :     return MSEdge::dictionary(id);
     511              : }
     512              : 
     513              : 
     514              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1