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