LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_FCDReplay.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.0 % 230 207
Test Date: 2025-11-13 15:38:19 Functions: 93.8 % 16 15

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2013-2025 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              : MSDevice_FCDReplay::FCDHandler* MSDevice_FCDReplay::myHandler = nullptr;
      46              : SUMOSAXReader* MSDevice_FCDReplay::myParser = nullptr;
      47              : 
      48              : 
      49              : // ===========================================================================
      50              : // method definitions
      51              : // ===========================================================================
      52              : // ---------------------------------------------------------------------------
      53              : // static initialisation methods
      54              : // ---------------------------------------------------------------------------
      55              : void
      56        39784 : MSDevice_FCDReplay::insertOptions(OptionsCont& oc) {
      57        39784 :     oc.addOptionSubTopic("FCD Replay Device");
      58        79568 :     insertDefaultAssignmentOptions("fcd-replay", "FCD Replay Device", oc);
      59              : 
      60        39784 :     oc.doRegister("device.fcd-replay.file", new Option_FileName());
      61        79568 :     oc.addDescription("device.fcd-replay.file", "FCD Replay Device", TL("FCD file to read"));
      62        39784 : }
      63              : 
      64              : 
      65              : void
      66      5371063 : MSDevice_FCDReplay::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
      67      5371063 :     OptionsCont& oc = OptionsCont::getOptions();
      68     10742126 :     if (equippedByDefaultAssignmentOptions(oc, "fcd-replay", v, oc.isSet("device.fcd-replay.file"))) {
      69           36 :         MSDevice_FCDReplay* device = new MSDevice_FCDReplay(v, "fcdReplay_" + v.getID());
      70           36 :         into.push_back(device);
      71              :     }
      72      5371063 : }
      73              : 
      74              : 
      75              : void
      76        37650 : MSDevice_FCDReplay::init() {
      77        37650 :     delete myHandler;
      78        37650 :     myHandler = nullptr;
      79        37650 :     const OptionsCont& oc = OptionsCont::getOptions();
      80        75300 :     if (oc.isSet("device.fcd-replay.file")) {
      81           24 :         const std::string& filename = oc.getString("device.fcd-replay.file");
      82           24 :         myHandler = new MSDevice_FCDReplay::FCDHandler(filename);
      83           24 :         myParser = XMLSubSys::getSAXReader(*myHandler);
      84           48 :         if (!myParser->parseFirst(filename)) {
      85            0 :             throw ProcessError(TLF("Can not read XML-file '%'.", filename));
      86              :         }
      87           24 :         const SUMOTime inc = parseNext(SIMSTEP);
      88           24 :         MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(new MoveVehicles(), SIMSTEP + DELTA_T);
      89           24 :         if (inc > 0) {
      90            8 :             MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(new StaticCommand<MSDevice_FCDReplay>(&MSDevice_FCDReplay::parseNext),
      91            4 :                     SIMSTEP + inc);
      92              :         }
      93              :     }
      94        37650 : }
      95              : 
      96              : 
      97              : SUMOTime
      98           28 : MSDevice_FCDReplay::parseNext(SUMOTime t) {
      99           28 :     SUMOTime inc = string2time(OptionsCont::getOptions().getString("route-steps"));
     100              :     // make sure that we have always at least inc time steps buffered, so at time 200 we will parse 400 to 600
     101           28 :     const SUMOTime start = myHandler->getTime();
     102        56340 :     while (myHandler->getTime() < t + 2 * inc) {
     103        56336 :         if (!myParser->parseNext()) {
     104              :             inc = 0;
     105              :             break;
     106              :         }
     107              :     }
     108           28 :     myHandler->updateTrafficObjects(start);
     109           28 :     return inc;
     110              : }
     111              : 
     112              : 
     113              : // ---------------------------------------------------------------------------
     114              : // MSDevice_FCDReplay-methods
     115              : // ---------------------------------------------------------------------------
     116           36 : MSDevice_FCDReplay::MSDevice_FCDReplay(SUMOVehicle& holder, const std::string& id) :
     117           36 :     MSVehicleDevice(holder, id) {
     118           36 : }
     119              : 
     120              : 
     121           72 : MSDevice_FCDReplay::~MSDevice_FCDReplay() {
     122           72 : }
     123              : 
     124              : 
     125              : void
     126         3304 : MSDevice_FCDReplay::move(SUMOTime currentTime) {
     127         3304 :     if (myTrajectory == nullptr) {
     128            8 :         auto it = myHandler->getTrajectories().find(myHolder.getID());
     129            8 :         if (it == myHandler->getTrajectories().end()) {
     130              :             return;
     131              :         } else {
     132            8 :             const Trajectory& t = it->second;
     133              :             setTrajectory(&t);
     134              :         }
     135         3296 :     } else if (myTrajectoryIndex == (int)myTrajectory->size()) {
     136              :         // removal happens via the usual MSVehicle::hasArrived mechanism
     137              :         // TODO we may need to set an arrivalPos
     138              :         return;
     139              :     }
     140         3272 :     MSVehicle* v = dynamic_cast<MSVehicle*>(&myHolder);
     141         3272 :     const TrajectoryEntry& te = myTrajectory->at(myTrajectoryIndex);
     142         3272 :     if (v == nullptr || te.time > currentTime) {
     143              :         return;
     144              :     }
     145         3260 :     if (te.edgeOrLane != "") {
     146         5400 :         const std::string& edgeID = SUMOXMLDefinitions::getEdgeIDFromLane(te.edgeOrLane);
     147         2700 :         const int laneIdx = SUMOXMLDefinitions::getIndexFromLane(te.edgeOrLane);
     148         2700 :         libsumo::Vehicle::moveToXY(myHolder.getID(), edgeID, laneIdx, te.pos.x(), te.pos.y(), te.angle, 7);
     149          560 :     } else if (te.pos.x() != libsumo::INVALID_DOUBLE_VALUE) {
     150            0 :         libsumo::Vehicle::moveToXY(myHolder.getID(), "", -1, te.pos.x(), te.pos.y(), te.angle, 0);
     151              :     }
     152         3260 :     v->getInfluencer().setSpeedMode(0);
     153         3260 :     libsumo::Vehicle::setSpeed(myHolder.getID(), te.speed);
     154              :     // libsumo::Vehicle::changeLane(myHolder.getID(), laneIdx, TS);
     155         3260 :     myTrajectoryIndex++;
     156              : }
     157              : 
     158              : 
     159              : SUMOTime
     160         3980 : MSDevice_FCDReplay::MoveVehicles::execute(SUMOTime currentTime) {
     161         3980 :     MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
     162         9884 :     for (MSVehicleControl::constVehIt i = c.loadedVehBegin(); i != c.loadedVehEnd(); ++i) {
     163         5904 :         MSDevice_FCDReplay* device = static_cast<MSDevice_FCDReplay*>(i->second->getDevice(typeid(MSDevice_FCDReplay)));
     164         5904 :         if (device != nullptr && i->second->hasDeparted()) {
     165         3304 :             device->move(currentTime);
     166              :         }
     167              :     }
     168         3980 :     return DELTA_T;
     169              : }
     170              : 
     171              : 
     172              : // ---------------------------------------------------------------------------
     173              : // MSDevice_FCDReplay::FCDHandler-methods
     174              : // ---------------------------------------------------------------------------
     175           24 : MSDevice_FCDReplay::FCDHandler::FCDHandler(const std::string& file) :
     176              :     SUMOSAXHandler(file),
     177              :     MapMatcher(false, false,
     178           24 :                OptionsCont::getOptions().getFloat("mapmatch.distance"),
     179           96 :                MsgHandler::getErrorInstance()) {}
     180              : 
     181              : 
     182              : void
     183        24712 : MSDevice_FCDReplay::FCDHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
     184        24712 :     bool ok = true;
     185        24712 :     switch (element) {
     186         4072 :         case SUMO_TAG_TIMESTEP:
     187         4072 :             myTime = attrs.getSUMOTimeReporting(SUMO_ATTR_TIME, "", ok);
     188              :             myPositions.clear();
     189        24688 :             return;
     190        20616 :         case SUMO_TAG_VEHICLE:
     191              :         case SUMO_TAG_PERSON: {
     192        20616 :             if (myTime >= SIMSTEP) {
     193        20616 :                 const bool isPerson = element == SUMO_TAG_PERSON;
     194        20616 :                 const std::string id = attrs.getString(SUMO_ATTR_ID);
     195              :                 const Position xy = Position(attrs.getOpt<double>(SUMO_ATTR_X, id.c_str(), ok, libsumo::INVALID_DOUBLE_VALUE),
     196        20616 :                                              attrs.getOpt<double>(SUMO_ATTR_Y, id.c_str(), ok, libsumo::INVALID_DOUBLE_VALUE));
     197        20616 :                 const std::string type = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, id.c_str(), ok, "");
     198        44604 :                 const std::string edgeOrLane = attrs.getOpt<std::string>(isPerson ? SUMO_ATTR_EDGE : SUMO_ATTR_LANE, id.c_str(), ok, "");
     199        20616 :                 const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, id.c_str(), ok, libsumo::INVALID_DOUBLE_VALUE);
     200        20616 :                 const double pos = attrs.getOpt<double>(SUMO_ATTR_POSITION, id.c_str(), ok, libsumo::INVALID_DOUBLE_VALUE);
     201        20616 :                 const double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, libsumo::INVALID_DOUBLE_VALUE);
     202        20616 :                 std::string vehicle = attrs.getOpt<std::string>(SUMO_ATTR_VEHICLE, id.c_str(), ok, "");
     203        20616 :                 if (isPerson) {
     204        17244 :                     if (vehicle == "") {
     205              :                         const auto& veh = myPositions.find(xy);
     206        17244 :                         if (veh != myPositions.end()) {
     207         2788 :                             vehicle = veh->second;
     208              :                         }
     209              :                     }
     210              :                 } else {
     211         3372 :                     myPositions[xy] = id;
     212              :                 }
     213        20616 :                 myTrajectories[id].push_back({myTime, xy, edgeOrLane, pos, speed, angle});
     214        41232 :                 const MSEdge* edge = MSEdge::dictionary(isPerson ? edgeOrLane : SUMOXMLDefinitions::getEdgeIDFromLane(edgeOrLane));
     215        20616 :                 if (edge == nullptr && edgeOrLane != "") {
     216            0 :                     WRITE_WARNINGF(isPerson ? TL("Unknown edge '%' in fcd replay file for person '%'.") : TL("Unknown lane '%' in fcd replay file for vehicle '%'."), edgeOrLane, id);
     217              :                 }
     218              :                 if (myRoutes.count(id) == 0) {
     219          200 :                     if (edgeOrLane == "") {
     220            8 :                         const MSLane* const lane = getClosestLane(xy, SVC_PASSENGER);
     221            8 :                         if (lane != nullptr) {
     222            0 :                             myTrajectories[id].back().edgeOrLane = lane->getID();
     223            0 :                             myTrajectories[id].back().lanePos = lane->interpolateGeometryPosToLanePos(
     224              :                                                                     lane->getShape().nearest_offset_to_point25D(xy, false));
     225            0 :                             edge = &lane->getEdge();
     226            0 :                             while (edge->isInternal()) {  // we cannot use an internal edge for a route
     227            0 :                                 edge = edge->getSuccessors().front();
     228              :                             }
     229              :                         }
     230              :                     }
     231          600 :                     myRoutes[id] = std::make_tuple(myTime, type, isPerson, ConstMSEdgeVector{edge}, std::vector<StageStart>());
     232              :                 } else {
     233        20416 :                     ConstMSEdgeVector& route = std::get<3>(myRoutes[id]);
     234        19856 :                     if (edge != nullptr && !edge->isInternal() && edge != route.back()) {
     235         1288 :                         route.push_back(edge);
     236              :                     }
     237              :                 }
     238        20616 :                 std::vector<StageStart>& vehicleUsage = std::get<4>(myRoutes[id]);
     239        20616 :                 if ((vehicleUsage.empty() && vehicle != "") || (!vehicleUsage.empty() && vehicle != vehicleUsage.back().vehicle)) {
     240          160 :                     vehicleUsage.push_back({vehicle, (int)myTrajectories[id].size() - 1, (int)std::get<3>(myRoutes[id]).size() - 1});
     241              :                 }
     242              :             }
     243              :             return;
     244              :         }
     245              :         default:
     246              :             break;
     247              :     }
     248        41312 : }
     249              : 
     250              : 
     251              : MSTransportable::MSTransportablePlan*
     252          188 : MSDevice_FCDReplay::FCDHandler::makePlan(const SUMOVehicleParameter& params, const ConstMSEdgeVector& route,
     253              :         const std::vector<StageStart>& stages, const Trajectory& t) {
     254          188 :     MSTransportable::MSTransportablePlan* plan = new MSTransportable::MSTransportablePlan();
     255          376 :     plan->push_back(new MSStageWaiting(route.front(), nullptr, 0, params.depart, params.departPos, "awaiting departure", true));
     256              :     int prevRouteOffset = 0;
     257          188 :     const MSEdge* start = route.front();
     258              :     std::string prevVeh;
     259          316 :     for (const auto& stageStart : stages) {
     260          128 :         if (stageStart.vehicle != prevVeh) {
     261          128 :             if (stageStart.trajectoryOffset != 0) {
     262          128 :                 const MSEdge* prevEdge = MSEdge::dictionary(t[stageStart.trajectoryOffset - 1].edgeOrLane);
     263          128 :                 if (prevVeh == "") {
     264              :                     MSStoppingPlace* finalStop = nullptr;
     265           64 :                     if (prevRouteOffset < (int)route.size() - 1 && (route[prevRouteOffset]->getPermissions() & SVC_PEDESTRIAN) == 0) {
     266            0 :                         prevRouteOffset++;  // skip the access
     267              :                     }
     268           64 :                     int offset = stageStart.routeOffset;
     269           64 :                     if (offset < (int)route.size() - 1 && (route[offset]->getPermissions() & SVC_PEDESTRIAN) == SVC_PEDESTRIAN) {
     270            0 :                         offset++;  // a bus stop or two consecutive walks so include the edge in both
     271              :                     } else {
     272              :                         // may have an access, let's find the stop
     273           64 :                         const std::string& stop = MSNet::getInstance()->getStoppingPlaceID(route[offset]->getLanes()[0], t[stageStart.trajectoryOffset].lanePos, SUMO_TAG_BUS_STOP);
     274           64 :                         if (stop != "") {
     275           64 :                             finalStop = MSNet::getInstance()->getStoppingPlace(stop, SUMO_TAG_BUS_STOP);
     276              :                         }
     277              :                     }
     278           64 :                     ConstMSEdgeVector subRoute = ConstMSEdgeVector(route.begin() + prevRouteOffset, route.begin() + offset);
     279           64 :                     plan->push_back(new MSStageWalking(params.id, subRoute, finalStop, -1, params.departSpeed, params.departPos, 0, 0));
     280           64 :                 } else {
     281          192 :                     plan->push_back(new MSStageDriving(start, prevEdge, nullptr, -1, 0, {prevVeh}));
     282              :                 }
     283          128 :                 start = MSEdge::dictionary(t[stageStart.trajectoryOffset].edgeOrLane);
     284              :                 prevVeh = stageStart.vehicle;
     285          128 :                 prevRouteOffset = stageStart.routeOffset;
     286              :             }
     287              :         }
     288              :     }
     289              :     // final stage
     290          188 :     if (prevVeh == "") {
     291          188 :         if (prevRouteOffset < (int)route.size() - 1 && (route[prevRouteOffset]->getPermissions() & SVC_PEDESTRIAN) == 0) {
     292           64 :             prevRouteOffset++;
     293              :         }
     294              :         int offset = (int)route.size() - 1;
     295          188 :         if ((route[offset]->getPermissions() & SVC_PEDESTRIAN) == SVC_PEDESTRIAN) {
     296              :             offset++;
     297              :         }
     298          188 :         if (prevRouteOffset < offset) {
     299              :             // otherwise we may be still in a vehicle or in access, skip the stage for now
     300          188 :             ConstMSEdgeVector subRoute = ConstMSEdgeVector(route.begin() + prevRouteOffset, route.begin() + offset);
     301          188 :             plan->push_back(new MSStageWalking(params.id, subRoute, nullptr, -1, params.departSpeed, params.departPos, 0, 0));
     302          188 :         }
     303              :     } else {
     304            0 :         plan->push_back(new MSStageDriving(start, route.back(), nullptr, -1, 0, {prevVeh}));
     305              :     }
     306          188 :     return plan;
     307              : }
     308              : 
     309              : 
     310              : ConstMSEdgeVector
     311           32 : MSDevice_FCDReplay::FCDHandler::checkRoute(const ConstMSEdgeVector& edges, const SUMOVehicle* const vehicle) {
     312              :     ConstMSEdgeVector checkedRoute;
     313          464 :     for (const MSEdge* const e : edges) {
     314          432 :         if (checkedRoute.empty() || checkedRoute.back()->isConnectedTo(*e, vehicle->getVehicleType().getVehicleClass())) {
     315          424 :             checkedRoute.push_back(e);
     316              :         } else {
     317            8 :             const MSEdge* fromEdge = checkedRoute.back();
     318              :             checkedRoute.pop_back();
     319           16 :             if (!MSNet::getInstance()->getRouterTT(0).compute(fromEdge, e, vehicle, myTime, checkedRoute)) {
     320              :                 // TODO maybe warn about disconnected route
     321            4 :                 checkedRoute.push_back(fromEdge);
     322            4 :                 checkedRoute.push_back(e);
     323              :             }
     324              :             // TODO check whether we introduced a big detour
     325              :         }
     326              :     }
     327           32 :     return checkedRoute;
     328            0 : }
     329              : 
     330              : 
     331              : void
     332           28 : MSDevice_FCDReplay::FCDHandler::updateTrafficObjects(const SUMOTime intervalStart) {
     333          280 :     for (const auto& desc : myRoutes) {
     334          252 :         const std::string& id = desc.first;
     335          252 :         const bool isPerson = std::get<2>(desc.second);
     336              :         const ConstMSEdgeVector& routeEdges = std::get<3>(desc.second);
     337          252 :         Trajectory& t = myTrajectories[id];
     338          252 :         if (t.front().time >= intervalStart) {
     339              :             // new vehicle or person
     340          200 :             SUMOVehicleParameter* params = new SUMOVehicleParameter();
     341          200 :             params->id = id;
     342          200 :             params->depart = std::get<0>(desc.second);
     343          200 :             params->departPos = t.front().lanePos;
     344          200 :             params->departSpeed = t.front().speed;
     345              :             // params->arrivalPos = t.back().lanePos;
     346              :             std::string vType = std::get<1>(desc.second);
     347          200 :             if (vType == "") {
     348           48 :                 vType = isPerson ? DEFAULT_PEDTYPE_ID : DEFAULT_VTYPE_ID;
     349              :             }
     350          200 :             MSVehicleType* vehicleType = MSNet::getInstance()->getVehicleControl().getVType(vType);
     351          200 :             if (vehicleType == nullptr) {
     352            0 :                 throw ProcessError(TLF("Unknown vType '%'.", vType));
     353              :             }
     354          200 :             if (routeEdges.front() == nullptr) {
     355            8 :                 if (isPerson) {
     356            0 :                     WRITE_WARNINGF(TL("No edge in fcd replay file for person '%' at time %."), id, time2string(t.front().time));
     357              :                 } else {
     358           24 :                     WRITE_WARNINGF(TL("No lane in fcd replay file for vehicle '%' at time %."), id, time2string(t.front().time));
     359              :                 }
     360            8 :                 continue;
     361              :             }
     362          192 :             if (isPerson) {
     363          164 :                 MSTransportable::MSTransportablePlan* plan = makePlan(*params, routeEdges, std::get<4>(desc.second), t);
     364              :                 // plan completed, now build the person
     365          164 :                 MSTransportable* person = MSNet::getInstance()->getPersonControl().buildPerson(params, vehicleType, plan, nullptr);
     366          164 :                 person->getSingularType().setVClass(SVC_IGNORING);
     367          164 :                 if (!MSNet::getInstance()->getPersonControl().add(person)) {
     368            0 :                     throw ProcessError(TLF("Duplicate person '%'.", id));
     369              :                 }
     370          164 :                 MSTransportableDevice_FCDReplay* device = static_cast<MSTransportableDevice_FCDReplay*>(person->getDevice(typeid(MSTransportableDevice_FCDReplay)));
     371          164 :                 if (device == nullptr) {  // Person did not get a replay device
     372              :                     // TODO delete person
     373            0 :                     continue;
     374              :                 }
     375              :                 device->setTrajectory(&t);
     376              :             } else {
     377           28 :                 const std::string dummyRouteID = "DUMMY_ROUTE_" + id;
     378              :                 const StopParVector stops;
     379           28 :                 ConstMSRoutePtr route = std::make_shared<MSRoute>(dummyRouteID, routeEdges, true, nullptr, stops);
     380           56 :                 if (!MSRoute::dictionary(dummyRouteID, route)) {
     381            0 :                     throw ProcessError(TLF("Could not add route '%'.", dummyRouteID));
     382              :                 }
     383           28 :                 if (t.front().edgeOrLane != "") {
     384           28 :                     params->departPosProcedure = DepartPosDefinition::GIVEN;
     385           28 :                     params->departLaneProcedure = DepartLaneDefinition::GIVEN;
     386           28 :                     params->departLane = SUMOXMLDefinitions::getIndexFromLane(t.front().edgeOrLane);
     387              :                 }
     388           28 :                 SUMOVehicle* vehicle = MSNet::getInstance()->getVehicleControl().buildVehicle(params, route, vehicleType, false, MSVehicleControl::VehicleDefinitionSource::OTHER);
     389           28 :                 if (!MSNet::getInstance()->getVehicleControl().addVehicle(id, vehicle)) {
     390            0 :                     throw ProcessError(TLF("Duplicate vehicle '%'.", id));
     391              :                 }
     392           28 :                 MSNet::getInstance()->getInsertionControl().add(vehicle);
     393           28 :                 MSDevice_FCDReplay* device = static_cast<MSDevice_FCDReplay*>(vehicle->getDevice(typeid(MSDevice_FCDReplay)));
     394            0 :                 if (device == nullptr) {  // Vehicle did not get a replay device
     395            0 :                     MSNet::getInstance()->getVehicleControl().deleteVehicle(vehicle, true);
     396              :                     continue;
     397              :                 }
     398              :                 device->setTrajectory(&t);
     399              : 
     400              :                 // repair the route, cannot do this on parsing because a vehicle is needed
     401           28 :                 ConstMSEdgeVector checkedRoute = checkRoute(routeEdges, vehicle);
     402           28 :                 if (checkedRoute.size() != routeEdges.size()) {
     403            8 :                     vehicle->replaceRouteEdges(checkedRoute, -1, 0, "FCDReplay", true);
     404              :                 }
     405           56 :             }
     406           52 :         } else if (t.back().time >= intervalStart) {
     407              :             // new data for existing person / vehicle
     408           28 :             if (isPerson) {
     409           24 :                 MSTransportable* person = MSNet::getInstance()->getPersonControl().get(id);
     410           24 :                 if (person == nullptr) {
     411              :                     // TODO this should not happen
     412            0 :                     continue;
     413              :                 }
     414              :                 // TODO optimize: no need to regenerate the whole plan
     415           24 :                 MSTransportable::MSTransportablePlan* plan = makePlan(person->getParameter(), routeEdges, std::get<4>(desc.second), t);
     416           24 :                 const int stageIndex = person->getNumRemainingStages() - 1;
     417              :                 MSStage* const final = person->getNextStage(stageIndex);
     418              :                 bool append = false;
     419          120 :                 for (MSStage* stage : *plan) {
     420           96 :                     if (stage->getStageType() == final->getStageType() && stage->getFromEdge() == final->getFromEdge()) {
     421              :                         // TODO: circular plans?
     422              :                         append = true;
     423              :                     }
     424           72 :                     if (append) {
     425           24 :                         person->appendStage(stage);
     426              :                     }
     427              :                 }
     428           24 :                 person->removeStage(stageIndex);
     429              :             } else {
     430            4 :                 SUMOVehicle* vehicle = MSNet::getInstance()->getVehicleControl().getVehicle(id);
     431            4 :                 ConstMSEdgeVector checkedRoute = checkRoute(routeEdges, vehicle);
     432            4 :                 if ((int)checkedRoute.size() != vehicle->getRoute().size()) {
     433            8 :                     vehicle->replaceRouteEdges(checkedRoute, -1, 0, "FCDReplay", true);
     434              :                 }
     435            4 :             }
     436              :         }
     437              :     }
     438           28 : }
     439              : 
     440              : 
     441              : void
     442            4 : MSDevice_FCDReplay::FCDHandler::initLaneTree(NamedRTree* tree) {
     443           64 :     for (const auto& edge : MSEdge::getAllEdges()) {
     444          156 :         for (MSLane* lane : edge->getLanes()) {
     445           96 :             Boundary b = lane->getShape().getBoxBoundary();
     446           96 :             const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
     447           96 :             const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
     448           96 :             tree->Insert(cmin, cmax, lane);
     449              :         }
     450              :     }
     451            4 : }
     452              : 
     453              : 
     454              : MSEdge*
     455            0 : MSDevice_FCDReplay::FCDHandler::retrieveEdge(const std::string& id) {
     456            0 :     return MSEdge::dictionary(id);
     457              : }
     458              : 
     459              : 
     460              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1