1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    MSInductLoop.cpp
11 /// @author  Christian Roessel
12 /// @author  Daniel Krajzewicz
13 /// @author  Jakob Erdmann
14 /// @author  Sascha Krieg
15 /// @author  Michael Behrisch
16 /// @author  Laura Bieker
17 /// @date    2004-11-23
18 /// @version $Id$
19 ///
20 // An unextended detector measuring at a fixed position on a fixed lane.
21 /****************************************************************************/
22 
23 
24 // ===========================================================================
25 // included modules
26 // ===========================================================================
27 #include <config.h>
28 
29 #include "MSInductLoop.h"
30 #include <cassert>
31 #include <numeric>
32 #include <utility>
33 #include <utils/common/WrappingCommand.h>
34 #include <utils/common/ToString.h>
35 #include <microsim/MSEventControl.h>
36 #include <microsim/MSLane.h>
37 #include <microsim/MSVehicle.h>
38 #include <microsim/MSNet.h>
39 #include <utils/common/MsgHandler.h>
40 #include <utils/common/UtilExceptions.h>
41 #include <utils/common/StringUtils.h>
42 #include <utils/iodevices/OutputDevice.h>
43 
44 #define HAS_NOT_LEFT_DETECTOR -1
45 
46 // ===========================================================================
47 // method definitions
48 // ===========================================================================
MSInductLoop(const std::string & id,MSLane * const lane,double positionInMeters,const std::string & vTypes)49 MSInductLoop::MSInductLoop(const std::string& id, MSLane* const lane,
50                            double positionInMeters,
51                            const std::string& vTypes) :
52     MSMoveReminder(id, lane),
53     MSDetectorFileOutput(id, vTypes),
54     myPosition(positionInMeters),
55     myLastLeaveTime(SIMTIME),
56     myVehicleDataCont(),
57     myVehiclesOnDet() {
58     assert(myPosition >= 0 && myPosition <= myLane->getLength());
59     reset();
60 }
61 
62 
~MSInductLoop()63 MSInductLoop::~MSInductLoop() {
64 }
65 
66 
67 void
reset()68 MSInductLoop::reset() {
69     myEnteredVehicleNumber = 0;
70     myLastVehicleDataCont = myVehicleDataCont;
71     myVehicleDataCont.clear();
72 }
73 
74 
75 bool
notifyEnter(SUMOTrafficObject & veh,Notification reason,const MSLane *)76 MSInductLoop::notifyEnter(SUMOTrafficObject& veh, Notification reason, const MSLane* /* enteredLane */) {
77     if (!vehicleApplies(veh)) {
78         return false;
79     }
80     if (reason == NOTIFICATION_DEPARTED ||
81             reason == NOTIFICATION_TELEPORT ||
82             reason == NOTIFICATION_PARKING ||
83             reason == NOTIFICATION_LANE_CHANGE) {
84         if (veh.getPositionOnLane() >= myPosition && veh.getBackPositionOnLane(myLane) < myPosition) {
85             myVehiclesOnDet.insert(std::make_pair(&veh, SIMTIME));
86             myEnteredVehicleNumber++;
87         }
88     }
89     return true;
90 }
91 
92 
93 bool
notifyMove(SUMOTrafficObject & veh,double oldPos,double newPos,double newSpeed)94 MSInductLoop::notifyMove(SUMOTrafficObject& veh, double oldPos,
95                          double newPos, double newSpeed) {
96     if (newPos < myPosition) {
97         // detector not reached yet
98         return true;
99     }
100     const double oldSpeed = veh.getPreviousSpeed();
101     if (newPos >= myPosition && oldPos < myPosition) {
102         // entered the detector by move
103         const double timeBeforeEnter = MSCFModel::passingTime(oldPos, myPosition, newPos, oldSpeed, newSpeed);
104         double entryTime = SIMTIME + timeBeforeEnter;
105         enterDetectorByMove(veh, entryTime);
106     }
107     double oldBackPos = oldPos - veh.getVehicleType().getLength();
108     double newBackPos = newPos - veh.getVehicleType().getLength();
109     if (newBackPos > myPosition) {
110         // vehicle passed the detector (it may have changed onto this lane somewhere past the detector)
111         assert(!MSGlobals::gSemiImplicitEulerUpdate || newSpeed > 0 || myVehiclesOnDet.find(&veh) == myVehiclesOnDet.end());
112         if (oldBackPos <= myPosition) {
113             const double timeBeforeLeave = MSCFModel::passingTime(oldBackPos, myPosition, newBackPos, oldSpeed, newSpeed);
114             const double leaveTime = SIMTIME + timeBeforeLeave;
115             leaveDetectorByMove(veh, leaveTime);
116         } else {
117             // vehicle is already beyond the detector...
118             // This can happen even if it is still registered in myVehiclesOnDet, e.g., after teleport.
119             // XXX: would we need to call leaveDetectorByMove(veh, leaveTime) as it was done before
120             //      I inserted this if-else differentiation? (Leo) It seems that such a call only resets
121             //      the last leave Time, which seems inadequate to do for such a situation (though it actually
122             //      appears in test output/e1/one_vehicle/lane_change). Moreover, if the vehicle was
123             //      not removed, this call would tidy up.
124             // XXX: Indeed, we need to tidy up, e.g., in case of teleport insertion behind detector
125             // XXX: As a quickfix we just remove it. (should be discussed! Leo) Refs. #2579
126 
127             myVehiclesOnDet.erase(&veh);
128         }
129         return false;
130     }
131     // vehicle stays on the detector
132     return true;
133 }
134 
135 
136 bool
notifyLeave(SUMOTrafficObject & veh,double lastPos,MSMoveReminder::Notification reason,const MSLane *)137 MSInductLoop::notifyLeave(SUMOTrafficObject& veh, double lastPos, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
138     if (reason != MSMoveReminder::NOTIFICATION_JUNCTION) {
139         leaveDetectorByLaneChange(veh, lastPos);
140         return false;
141     }
142     return true;
143 }
144 
145 
146 double
getCurrentSpeed() const147 MSInductLoop::getCurrentSpeed() const {
148     std::vector<VehicleData> d = collectVehiclesOnDet(MSNet::getInstance()->getCurrentTimeStep() - DELTA_T);
149     return d.size() != 0
150            ? std::accumulate(d.begin(), d.end(), (double) 0.0, speedSum) / (double) d.size()
151            : -1;
152 }
153 
154 
155 double
getCurrentLength() const156 MSInductLoop::getCurrentLength() const {
157     std::vector<VehicleData> d = collectVehiclesOnDet(MSNet::getInstance()->getCurrentTimeStep() - DELTA_T);
158     return d.size() != 0
159            ? std::accumulate(d.begin(), d.end(), (double) 0.0, lengthSum) / (double) d.size()
160            : -1;
161 }
162 
163 
164 double
getCurrentOccupancy() const165 MSInductLoop::getCurrentOccupancy() const {
166     SUMOTime tbeg = MSNet::getInstance()->getCurrentTimeStep() - DELTA_T;
167     std::vector<VehicleData> d = collectVehiclesOnDet(tbeg);
168     if (d.size() == 0) {
169         return -1;
170     }
171     double occupancy = 0;
172     double csecond = STEPS2TIME(MSNet::getInstance()->getCurrentTimeStep());
173     for (std::vector< VehicleData >::const_iterator i = d.begin(); i != d.end(); ++i) {
174         const double leaveTime = (*i).leaveTimeM == HAS_NOT_LEFT_DETECTOR ? csecond : (*i).leaveTimeM;
175         const double timeOnDetDuringInterval = leaveTime - MAX2(STEPS2TIME(tbeg), (*i).entryTimeM);
176         occupancy += MIN2(timeOnDetDuringInterval, TS);
177     }
178     return occupancy / TS * (double) 100.;
179 }
180 
181 
182 int
getCurrentPassedNumber() const183 MSInductLoop::getCurrentPassedNumber() const {
184     std::vector<VehicleData> d = collectVehiclesOnDet(MSNet::getInstance()->getCurrentTimeStep() - DELTA_T);
185     return (int) d.size();
186 }
187 
188 
189 std::vector<std::string>
getCurrentVehicleIDs() const190 MSInductLoop::getCurrentVehicleIDs() const {
191     std::vector<VehicleData> d = collectVehiclesOnDet(MSNet::getInstance()->getCurrentTimeStep() - DELTA_T);
192     std::vector<std::string> ret;
193     for (std::vector<VehicleData>::iterator i = d.begin(); i != d.end(); ++i) {
194         ret.push_back((*i).idM);
195     }
196     return ret;
197 }
198 
199 
200 double
getTimeSinceLastDetection() const201 MSInductLoop::getTimeSinceLastDetection() const {
202     if (myVehiclesOnDet.size() != 0) {
203         // detector is occupied
204         return 0;
205     }
206     return SIMTIME - myLastLeaveTime;
207 }
208 
209 
210 void
writeXMLDetectorProlog(OutputDevice & dev) const211 MSInductLoop::writeXMLDetectorProlog(OutputDevice& dev) const {
212     dev.writeXMLHeader("detector", "det_e1_file.xsd");
213 }
214 
215 
216 void
writeXMLOutput(OutputDevice & dev,SUMOTime startTime,SUMOTime stopTime)217 MSInductLoop::writeXMLOutput(OutputDevice& dev,
218                              SUMOTime startTime, SUMOTime stopTime) {
219     const double t(STEPS2TIME(stopTime - startTime));
220     const double flow = ((double)myVehicleDataCont.size() / t) * (double) 3600.0;
221     double occupancy = 0.;
222     double speedSum = 0.;
223     double lengthSum = 0.;
224     // to approximate the space mean speed
225     double inverseSpeedSum = 0.;
226     for (std::deque< VehicleData >::const_iterator i = myVehicleDataCont.begin(); i != myVehicleDataCont.end(); ++i) {
227         const double timeOnDetDuringInterval = i->leaveTimeM - MAX2(STEPS2TIME(startTime), i->entryTimeM);
228         occupancy += MIN2(timeOnDetDuringInterval, t);
229         speedSum += i->speedM;
230         assert(i->speedM > 0);
231         inverseSpeedSum += 1. / i->speedM;
232         lengthSum += i->lengthM;
233     }
234     for (std::map< SUMOTrafficObject*, double >::const_iterator i = myVehiclesOnDet.begin(); i != myVehiclesOnDet.end(); ++i) {
235         occupancy += STEPS2TIME(stopTime) - MAX2(STEPS2TIME(startTime), i->second);
236     }
237     occupancy = occupancy / t * (double) 100.;
238     const double meanSpeed = myVehicleDataCont.size() != 0 ? speedSum / (double)myVehicleDataCont.size() : -1;
239     const double harmonicMeanSpeed = myVehicleDataCont.size() != 0 ? (double)myVehicleDataCont.size() / inverseSpeedSum : -1;
240     const double meanLength = myVehicleDataCont.size() != 0 ? lengthSum / (double)myVehicleDataCont.size() : -1;
241     dev.openTag(SUMO_TAG_INTERVAL).writeAttr(SUMO_ATTR_BEGIN, STEPS2TIME(startTime)).writeAttr(SUMO_ATTR_END, STEPS2TIME(stopTime));
242     dev.writeAttr(SUMO_ATTR_ID, StringUtils::escapeXML(getID())).writeAttr("nVehContrib", myVehicleDataCont.size());
243     dev.writeAttr("flow", flow).writeAttr("occupancy", occupancy).writeAttr("speed", meanSpeed).writeAttr("harmonicMeanSpeed", harmonicMeanSpeed);
244     dev.writeAttr("length", meanLength).writeAttr("nVehEntered", myEnteredVehicleNumber).closeTag();
245     reset();
246 }
247 
248 
249 void
enterDetectorByMove(SUMOTrafficObject & veh,double entryTimestep)250 MSInductLoop::enterDetectorByMove(SUMOTrafficObject& veh,
251                                   double entryTimestep) {
252 //    // Debug (Leo)
253 //    std::cout << "enterDetectorByMove(), detector = '"<< myID <<"', veh = '" << veh.getID() << "'\n";
254 
255     myVehiclesOnDet.insert(std::make_pair(&veh, entryTimestep));
256     myEnteredVehicleNumber++;
257 }
258 
259 
260 void
leaveDetectorByMove(SUMOTrafficObject & veh,double leaveTimestep)261 MSInductLoop::leaveDetectorByMove(SUMOTrafficObject& veh,
262                                   double leaveTimestep) {
263 
264 //    // Debug (Leo)
265 //    std::cout << "leaveDetectorByMove(), detector = '"<< myID <<"', veh = '" << veh.getID() << "'\n";
266 
267     VehicleMap::iterator it = myVehiclesOnDet.find(&veh);
268     if (it != myVehiclesOnDet.end()) {
269         double entryTimestep = it->second;
270         myVehiclesOnDet.erase(it);
271         assert(entryTimestep < leaveTimestep);
272         myVehicleDataCont.push_back(VehicleData(veh.getID(), veh.getVehicleType().getLength(), entryTimestep, leaveTimestep, veh.getVehicleType().getID()));
273         myLastOccupancy = leaveTimestep - entryTimestep;
274     }
275     // XXX: why is this outside the conditional block? (Leo)
276     myLastLeaveTime = leaveTimestep;
277 }
278 
279 
280 void
leaveDetectorByLaneChange(SUMOTrafficObject & veh,double)281 MSInductLoop::leaveDetectorByLaneChange(SUMOTrafficObject& veh, double /* lastPos */) {
282 
283 //    // Debug (Leo)
284 //    std::cout << "leaveDetectorByLaneChange(), detector = '"<< myID <<"', veh = '" << veh.getID() << "'\n";
285 
286     // Discard entry data
287     myVehiclesOnDet.erase(&veh);
288 }
289 
290 
291 std::vector<MSInductLoop::VehicleData>
collectVehiclesOnDet(SUMOTime tMS,bool leaveTime) const292 MSInductLoop::collectVehiclesOnDet(SUMOTime tMS, bool leaveTime) const {
293     double t = STEPS2TIME(tMS);
294     std::vector<VehicleData> ret;
295     for (VehicleDataCont::const_iterator i = myVehicleDataCont.begin(); i != myVehicleDataCont.end(); ++i) {
296         if ((*i).entryTimeM >= t || (leaveTime && (*i).leaveTimeM >= t)) {
297             ret.push_back(*i);
298         }
299     }
300     for (VehicleDataCont::const_iterator i = myLastVehicleDataCont.begin(); i != myLastVehicleDataCont.end(); ++i) {
301         if ((*i).entryTimeM >= t || (leaveTime && (*i).leaveTimeM >= t)) {
302             ret.push_back(*i);
303         }
304     }
305     for (VehicleMap::const_iterator i = myVehiclesOnDet.begin(); i != myVehiclesOnDet.end(); ++i) {
306         SUMOTrafficObject* v = (*i).first;
307         VehicleData d(v->getID(), v->getVehicleType().getLength(), (*i).second, HAS_NOT_LEFT_DETECTOR, v->getVehicleType().getID());
308         d.speedM = v->getSpeed();
309         ret.push_back(d);
310     }
311     return ret;
312 }
313 
314 
315 /****************************************************************************/
316 
317