Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-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 MSInductLoop.cpp
15 : /// @author Christian Roessel
16 : /// @author Daniel Krajzewicz
17 : /// @author Jakob Erdmann
18 : /// @author Sascha Krieg
19 : /// @author Michael Behrisch
20 : /// @author Laura Bieker
21 : /// @author Mirko Barthauer
22 : /// @date 2004-11-23
23 : ///
24 : // An unextended detector measuring at a fixed position on a fixed lane.
25 : /****************************************************************************/
26 : #include <config.h>
27 :
28 : #include "MSInductLoop.h"
29 : #include <cassert>
30 : #include <numeric>
31 : #include <utility>
32 : #ifdef HAVE_FOX
33 : #include <utils/common/ScopedLocker.h>
34 : #endif
35 : #include <utils/common/WrappingCommand.h>
36 : #include <utils/common/ToString.h>
37 : #include <microsim/MSEventControl.h>
38 : #include <microsim/MSLane.h>
39 : #include <microsim/MSEdge.h>
40 : #include <microsim/MSVehicle.h>
41 : #include <microsim/MSNet.h>
42 : #include <microsim/MSVehicleControl.h>
43 : #include <microsim/transportables/MSTransportable.h>
44 : #include <microsim/transportables/MSPModel.h>
45 : #include <mesosim/MELoop.h>
46 : #include <mesosim/MESegment.h>
47 : #include <mesosim/MEVehicle.h>
48 : #include <utils/common/MsgHandler.h>
49 : #include <utils/common/UtilExceptions.h>
50 : #include <utils/common/StringUtils.h>
51 : #include <utils/iodevices/OutputDevice.h>
52 :
53 : #define HAS_NOT_LEFT_DETECTOR -1
54 :
55 : //#define DEBUG_E1_NOTIFY_MOVE
56 :
57 : #define DEBUG_COND (true)
58 : //#define DEBUG_COND (isSelected())
59 : //#define DEBUG_COND (getID()=="")
60 :
61 : // ===========================================================================
62 : // method definitions
63 : // ===========================================================================
64 29935 : MSInductLoop::MSInductLoop(const std::string& id, MSLane* const lane,
65 : double positionInMeters,
66 : double length, std::string name,
67 : const std::string& vTypes,
68 : const std::string& nextEdges,
69 : int detectPersons,
70 29935 : const bool needLocking) :
71 : MSMoveReminder(id, lane),
72 : MSDetectorFileOutput(id, vTypes, nextEdges, detectPersons),
73 29931 : myName(name),
74 29931 : myPosition(positionInMeters),
75 29931 : myEndPosition(myPosition + length),
76 29931 : myNeedLock(needLocking || MSGlobals::gNumSimThreads > 1),
77 : // initialize in a way which doesn't impact actualted traffic lights at simulation start (yet doesn't look ugly in the outputs)
78 29931 : myLastLeaveTime(-3600),
79 29931 : myOverrideTime(-1),
80 29931 : myOverrideEntryTime(-1),
81 : myVehicleDataCont(),
82 : myVehiclesOnDet(),
83 29931 : myLastIntervalEnd(-1),
84 29935 : mySegment(nullptr) {
85 : assert(length >= 0);
86 : assert(myPosition >= 0 && myEndPosition <= myLane->getLength());
87 29931 : reset();
88 29931 : if (MSGlobals::gUseMesoSim) {
89 : // used by actuated tls
90 1586 : mySegment = MSGlobals::gMesoNet->getSegmentForEdge(lane->getEdge(), myPosition);
91 1586 : mySegment->addDetector(this);
92 1586 : mySegmentPos = myPosition - mySegment->getIndex() * mySegment->getLength();
93 : }
94 29935 : }
95 :
96 :
97 57410 : MSInductLoop::~MSInductLoop() {
98 87219 : }
99 :
100 :
101 : void
102 162823 : MSInductLoop::reset() {
103 : #ifdef HAVE_FOX
104 162823 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
105 : #endif
106 162823 : myEnteredVehicleNumber = 0;
107 162823 : myLastVehicleDataCont = myVehicleDataCont;
108 : myVehicleDataCont.clear();
109 162823 : myLastIntervalBegin = myLastIntervalEnd;
110 162823 : myLastIntervalEnd = SIMSTEP;
111 162823 : }
112 :
113 :
114 : bool
115 1404092 : MSInductLoop::notifyEnter(SUMOTrafficObject& veh, Notification reason, const MSLane* /* enteredLane */) {
116 : // vehicles must be kept if the "inductionloop" wants to detect passeengers
117 1404092 : if (!vehicleApplies(veh) && (veh.isPerson() || myDetectPersons <= (int)PersonMode::WALK)) {
118 : return false;
119 : }
120 1399141 : if (MSGlobals::gUseMesoSim) {
121 184720 : MEVehicle* mesoveh = dynamic_cast<MEVehicle*>(&veh);
122 : assert(mesoveh != nullptr);
123 : const MESegment* seg = mesoveh->getSegment();
124 184720 : if (seg != nullptr && (seg->numQueues() == 1 || mesoveh->getQueIndex() == myLane->getIndex())) {
125 160932 : const SUMOTime onSegTime = mesoveh->getEventTime() - mesoveh->getLastEntryTime();
126 : // extrapolate movement
127 160932 : const SUMOTime exLeaveTime = mesoveh->getLastEntryTime() + (SUMOTime)((double)onSegTime * mySegmentPos / seg->getLength());
128 : //std::cout << SIMTIME << " det=" << getID() << " veh=" << veh.getID() << " entry=" << STEPS2TIME(mesoveh->getLastEntryTime()) << " et=" << STEPS2TIME(mesoveh->getEventTime()) << " exLeaveTime=" << STEPS2TIME(exLeaveTime) << "\n";
129 160932 : myNextMesoLeaveTimes.push(exLeaveTime);
130 : }
131 184720 : return true;
132 : }
133 1214421 : if (reason != NOTIFICATION_JUNCTION) { // the junction case is handled in notifyMove
134 244896 : if (veh.getBackPositionOnLane(myLane) >= myPosition) {
135 : return false;
136 : }
137 242097 : if (veh.getPositionOnLane() >= myPosition) {
138 : #ifdef HAVE_FOX
139 1247 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
140 : #endif
141 1247 : myVehiclesOnDet[&veh] = SIMTIME;
142 1247 : myEnteredVehicleNumber++;
143 : }
144 : }
145 : return true;
146 : }
147 :
148 :
149 : bool
150 23844900 : MSInductLoop::notifyMove(SUMOTrafficObject& veh, double oldPos,
151 : double newPos, double newSpeed) {
152 23844900 : if (newPos < myPosition) {
153 : // detector not reached yet
154 : return true;
155 : }
156 2760515 : if (myDetectPersons > (int)PersonMode::WALK && !veh.isPerson()) {
157 : bool keep = false;
158 38 : MSBaseVehicle& v = dynamic_cast<MSBaseVehicle&>(veh);
159 76 : for (MSTransportable* p : v.getPersons()) {
160 38 : keep = notifyMove(*p, oldPos, newPos, newSpeed);
161 : }
162 38 : return keep;
163 : }
164 : #ifdef HAVE_FOX
165 2760477 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
166 : #endif
167 2760477 : const double oldSpeed = veh.getPreviousSpeed();
168 2760477 : if (newPos >= myPosition && oldPos < myPosition) {
169 : // entered the detector by move
170 1182675 : const double timeBeforeEnter = MSCFModel::passingTime(oldPos, myPosition, newPos, oldSpeed, newSpeed);
171 1182675 : myVehiclesOnDet[&veh] = SIMTIME + timeBeforeEnter;
172 1182675 : myEnteredVehicleNumber++;
173 : #ifdef DEBUG_E1_NOTIFY_MOVE
174 : if (DEBUG_COND) {
175 : std::cout << SIMTIME << " det=" << getID() << " enteredVeh=" << veh.getID() << "\n";
176 : }
177 : #endif
178 : }
179 2760477 : double oldBackPos = oldPos - veh.getVehicleType().getLength();
180 2760477 : double newBackPos = newPos - veh.getVehicleType().getLength();
181 2760477 : if (newBackPos > myEndPosition) {
182 : // vehicle passed the detector (it may have changed onto this lane somewhere past the detector)
183 : // assert(!MSGlobals::gSemiImplicitEulerUpdate || newSpeed > 0 || myVehiclesOnDet.find(&veh) == myVehiclesOnDet.end());
184 : // assertion is invalid in case of teleportation
185 1180300 : if (oldBackPos <= myEndPosition) {
186 : const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
187 1180296 : if (it != myVehiclesOnDet.end()) {
188 1180296 : const double entryTime = it->second;
189 1180296 : const double leaveTime = SIMTIME + MSCFModel::passingTime(oldBackPos, myEndPosition, newBackPos, oldSpeed, newSpeed);
190 : myVehiclesOnDet.erase(it);
191 : assert(entryTime <= leaveTime);
192 1180296 : myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, false, myEndPosition - myPosition));
193 1180296 : myLastLeaveTime = leaveTime;
194 : #ifdef DEBUG_E1_NOTIFY_MOVE
195 : if (DEBUG_COND) {
196 : std::cout << SIMTIME << " det=" << getID() << " leftVeh=" << veh.getID() << " oldBackPos=" << oldBackPos << " newBackPos=" << newBackPos << "\n";
197 : }
198 : #endif
199 : } else {
200 : #ifdef DEBUG_E1_NOTIFY_MOVE
201 : if (DEBUG_COND) {
202 : std::cout << SIMTIME << " det=" << getID() << " leftVeh=" << veh.getID() << " oldBackPos=" << oldBackPos << " newBackPos=" << newBackPos << " (notFound)\n";
203 : }
204 : #endif
205 : }
206 : } else {
207 : // vehicle is already beyond the detector...
208 : // This can happen even if it is still registered in myVehiclesOnDet, e.g., after teleport.
209 4 : myVehiclesOnDet.erase(&veh);
210 : #ifdef DEBUG_E1_NOTIFY_MOVE
211 : if (DEBUG_COND) {
212 : std::cout << SIMTIME << " det=" << getID() << " leftVeh=" << veh.getID() << " oldBackPos=" << oldBackPos << " newBackPos=" << newBackPos << " (unusual)\n";
213 : }
214 : #endif
215 : }
216 1180300 : return false;
217 : }
218 : // vehicle stays on the detector
219 : return true;
220 : }
221 :
222 :
223 : bool
224 1043657 : MSInductLoop::notifyLeave(SUMOTrafficObject& veh, double lastPos, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
225 1043657 : if (veh.isPerson() && myDetectPersons != (int)PersonMode::NONE) {
226 3203 : const int lastDir = lastPos < 0 ? MSPModel::BACKWARD : MSPModel::FORWARD;
227 3203 : notifyMovePerson(dynamic_cast<MSTransportable*>(&veh), lastDir, lastPos);
228 : }
229 1043657 : if (MSGlobals::gUseMesoSim && reason <= MSMoveReminder::NOTIFICATION_SEGMENT && vehicleApplies(veh)) {
230 184586 : MEVehicle* mesoveh = dynamic_cast<MEVehicle*>(&veh);
231 : assert(mesoveh != nullptr);
232 : const MESegment* seg = mesoveh->getSegment();
233 184586 : if (seg != nullptr && (seg->numQueues() == 1 || mesoveh->getQueIndex() == myLane->getIndex())) {
234 : // interpolate movement + 1 extra second
235 : // We can only interpolate the passing time at the detector after the vehicle leaves the segment. Consequently our information is always out of date
236 : // (by the time the current vehicle left, the follower vehicle should already have reached the detector so the estimated time gap to the last detection is always too large)
237 : // the prior implementation used seg->getBlockTime() which included the headway which somewhat worked around this deficit by accident
238 173051 : const SUMOTime inLeaveTime = mesoveh->getEventTime() - TIME2STEPS((seg->getLength() - mySegmentPos - mesoveh->getLength()) / myLane->getSpeedLimit());
239 : //std::cout << SIMTIME << " det=" << getID() << " veh=" << veh.getID() << " et=" << STEPS2TIME(mesoveh->getEventTime()) << " inLeaveTime=" << STEPS2TIME(inLeaveTime) << "\n";
240 160809 : auto q = seg->getQueue(mesoveh->getQueIndex());
241 160809 : if (q.size() > 1 && vehicleApplies(*q[q.size() - 2])) {
242 146135 : myLastMesoLeaveTimes.push(inLeaveTime + TIME2STEPS(seg->getLastHeadwaySeconds()));
243 : //std::cout << " inLeaveTime2=" << (STEPS2TIME(inLeaveTime) + seg->getLastHeadwaySeconds()) << "\n";
244 : }
245 160809 : myLastMesoLeaveTimes.push(inLeaveTime);
246 160809 : myEnteredVehicleNumber++;
247 160809 : }
248 184586 : return false;
249 : }
250 859071 : if (reason != MSMoveReminder::NOTIFICATION_JUNCTION || (veh.isPerson() && myDetectPersons != (int)PersonMode::NONE)) {
251 : #ifdef HAVE_FOX
252 31610 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
253 : #endif
254 : const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
255 31610 : if (it != myVehiclesOnDet.end()) {
256 3609 : const double entryTime = it->second;
257 3609 : const double leaveTime = SIMTIME + TS;
258 : myVehiclesOnDet.erase(it);
259 3609 : myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, true));
260 3609 : myLastLeaveTime = leaveTime;
261 : }
262 : return false;
263 : }
264 : return true;
265 : }
266 :
267 :
268 : double
269 145 : MSInductLoop::getSpeed(const int offset) const {
270 145 : const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
271 174 : return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, speedSum) / (double) d.size();
272 145 : }
273 :
274 :
275 : double
276 145 : MSInductLoop::getVehicleLength(const int offset) const {
277 145 : const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
278 174 : return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, lengthSum) / (double)d.size();
279 145 : }
280 :
281 :
282 : double
283 55851 : MSInductLoop::getOccupancy() const {
284 55851 : if (myOverrideTime >= 0) {
285 48 : return myOverrideTime < TS ? (TS - myOverrideTime) / TS * 100 : 0;
286 : }
287 55803 : const SUMOTime tbeg = SIMSTEP - DELTA_T;
288 : double occupancy = 0;
289 55803 : const double csecond = SIMTIME;
290 61647 : for (const VehicleData& i : collectVehiclesOnDet(tbeg, false, false, true)) {
291 5844 : const double leaveTime = i.leaveTimeM == HAS_NOT_LEFT_DETECTOR ? csecond : MIN2(i.leaveTimeM, csecond);
292 5844 : const double entryTime = MAX2(i.entryTimeM, STEPS2TIME(tbeg));
293 8615 : occupancy += MIN2(leaveTime - entryTime, TS);
294 55803 : }
295 55803 : return occupancy / TS * 100.;
296 : }
297 :
298 :
299 : double
300 3024 : MSInductLoop::getEnteredNumber(const int offset) const {
301 3024 : if (myOverrideTime >= 0) {
302 48 : return myOverrideTime < TS ? 1 : 0;
303 : }
304 2976 : return (double)collectVehiclesOnDet(SIMSTEP - offset, true, true).size();
305 : }
306 :
307 :
308 : std::vector<std::string>
309 875 : MSInductLoop::getVehicleIDs(const int offset) const {
310 : std::vector<std::string> ret;
311 1101 : for (const VehicleData& i : collectVehiclesOnDet(SIMSTEP - offset, true, true)) {
312 226 : ret.push_back(i.idM);
313 875 : }
314 875 : return ret;
315 0 : }
316 :
317 :
318 : double
319 1122418 : MSInductLoop::getTimeSinceLastDetection() const {
320 1122418 : if (myOverrideTime >= 0) {
321 : return myOverrideTime;
322 : }
323 1122370 : if (myVehiclesOnDet.size() != 0) {
324 : // detector is occupied
325 : return 0;
326 : }
327 1060821 : if (MSGlobals::gUseMesoSim) {
328 : //std::cout << SIMTIME << " det=" << getID() << " qBefore=" << myNextMesoLeaveTimes.size() << " last=" << STEPS2TIME(getLastDetectionTime()) << " qAfter=" << myNextMesoLeaveTimes.size() << " top=" << (myNextMesoLeaveTimes.empty() ? -1 : myNextMesoLeaveTimes.top()) << "\n";
329 440170 : return SIMTIME - STEPS2TIME(getLastDetectionTime());
330 : }
331 620651 : return SIMTIME - myLastLeaveTime;
332 : }
333 :
334 :
335 : void
336 16 : MSInductLoop::loadTimeSinceLastDetection(double time) {
337 16 : myLastLeaveTime = SIMTIME - time;
338 16 : }
339 :
340 :
341 : double
342 184760 : MSInductLoop::getOccupancyTime() const {
343 : #ifdef HAVE_FOX
344 184760 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
345 : #endif
346 184760 : if (myOverrideTime >= 0) {
347 0 : return SIMTIME - myOverrideEntryTime;
348 : }
349 184760 : if (myVehiclesOnDet.size() == 0) {
350 : // detector is unoccupied
351 : return 0;
352 : } else {
353 : double minEntry = std::numeric_limits<double>::max();
354 4530 : for (const auto& i : myVehiclesOnDet) {
355 2265 : minEntry = MIN2(i.second, minEntry);
356 : }
357 2265 : return SIMTIME - minEntry;
358 : }
359 : }
360 :
361 :
362 : double
363 2266 : MSInductLoop::getArrivalDelay() const {
364 : #ifdef HAVE_FOX
365 2266 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
366 : #endif
367 2266 : MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
368 : double result = -INVALID_DOUBLE;
369 2294 : for (const auto& item : collectVehiclesOnDet(SIMSTEP - DELTA_T)) {
370 28 : SUMOVehicle* v = vc.getVehicle(item.idM);
371 28 : if (v != nullptr) {
372 28 : MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(v);
373 28 : double ad = veh->getStopArrivalDelay();
374 28 : if (ad != INVALID_DOUBLE) {
375 : result = MAX2(result, ad);
376 : }
377 : }
378 2266 : }
379 2266 : return result;
380 : }
381 :
382 :
383 : void
384 501512 : MSInductLoop::discardOldTimes(TimeQueue& tq, SUMOTime now) {
385 : // discard times in the past except for the latest one
386 501512 : SUMOTime last = tq.top();
387 501512 : if (tq.size() > 1) {
388 : tq.pop();
389 743608 : while (tq.size() > 0 && tq.top() < now) {
390 455658 : last = tq.top();
391 : tq.pop();
392 : }
393 287950 : tq.push(last);
394 : }
395 501512 : }
396 :
397 :
398 : SUMOTime
399 849830 : MSInductLoop::getLastDetectionTime() const {
400 849830 : if (myOverrideTime >= 0) {
401 0 : return SIMSTEP - TIME2STEPS(myOverrideTime);
402 : }
403 849830 : if (myVehiclesOnDet.size() != 0) {
404 12696 : return MSNet::getInstance()->getCurrentTimeStep();
405 : }
406 837134 : const SUMOTime now = SIMSTEP;
407 : if (MSGlobals::gUseMesoSim
408 571624 : && !myNextMesoLeaveTimes.empty()
409 1089670 : && myNextMesoLeaveTimes.top() < now) {
410 252210 : discardOldTimes(myNextMesoLeaveTimes, now);
411 252210 : SUMOTime last = myNextMesoLeaveTimes.top();
412 252210 : if (!myLastMesoLeaveTimes.empty() && myLastMesoLeaveTimes.top() < now) {
413 249302 : discardOldTimes(myLastMesoLeaveTimes, now);
414 249302 : last = MAX2(last, myLastMesoLeaveTimes.top());
415 : }
416 : //std::cout << SIMTIME << " det=" << getID() << " result=" << last << " last=" << myNextMesoLeaveTimes.top() << " last2=" << (myLastMesoLeaveTimes.size() > 0 ? myLastMesoLeaveTimes.top() : -1) << "\n";
417 252210 : return last;
418 : }
419 1014946 : return TIME2STEPS(myLastLeaveTime);
420 : }
421 :
422 :
423 : double
424 732 : MSInductLoop::getIntervalOccupancy(bool lastInterval) const {
425 : double occupancy = 0;
426 732 : const double csecond = lastInterval ? STEPS2TIME(myLastIntervalEnd) : SIMTIME;
427 732 : const double aggTime = csecond - STEPS2TIME(lastInterval ? myLastIntervalBegin : myLastIntervalEnd);
428 732 : if (aggTime == 0) {
429 : return 0;
430 : }
431 3033 : for (const VehicleData& i : collectVehiclesOnDet(myLastIntervalEnd, false, false, true, lastInterval)) {
432 2304 : const double leaveTime = i.leaveTimeM == HAS_NOT_LEFT_DETECTOR ? csecond : MIN2(i.leaveTimeM, csecond);
433 2304 : const double entryTime = MAX2(i.entryTimeM, STEPS2TIME(lastInterval ? myLastIntervalBegin : myLastIntervalEnd));
434 2304 : occupancy += MIN2(leaveTime - entryTime, aggTime);
435 729 : }
436 729 : return occupancy / aggTime * 100.;
437 : }
438 :
439 :
440 : double
441 732 : MSInductLoop::getIntervalMeanSpeed(bool lastInterval) const {
442 732 : const std::vector<VehicleData>& d = collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval);
443 1236 : return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, speedSum) / (double) d.size();
444 732 : }
445 :
446 :
447 : int
448 732 : MSInductLoop::getIntervalVehicleNumber(bool lastInterval) const {
449 732 : return (int)collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval).size();
450 : }
451 :
452 :
453 : std::vector<std::string>
454 732 : MSInductLoop::getIntervalVehicleIDs(bool lastInterval) const {
455 : std::vector<std::string> ret;
456 3036 : for (const VehicleData& i : collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval)) {
457 2304 : ret.push_back(i.idM);
458 732 : }
459 732 : return ret;
460 0 : }
461 :
462 :
463 : void
464 20 : MSInductLoop::overrideTimeSinceDetection(double time) {
465 20 : myOverrideTime = time;
466 20 : if (time < 0) {
467 4 : myOverrideEntryTime = -1;
468 : } else {
469 16 : const double entryTime = MAX2(0.0, SIMTIME - time);
470 16 : if (myOverrideEntryTime >= 0) {
471 : // maintain earlier entry time to achive continous detection
472 12 : myOverrideEntryTime = MIN2(myOverrideEntryTime, entryTime);
473 : } else {
474 4 : myOverrideEntryTime = entryTime;
475 : }
476 : }
477 20 : }
478 :
479 : void
480 29927 : MSInductLoop::writeXMLDetectorProlog(OutputDevice& dev) const {
481 59854 : dev.writeXMLHeader("detector", "det_e1_file.xsd");
482 29927 : }
483 :
484 :
485 : void
486 132892 : MSInductLoop::writeXMLOutput(OutputDevice& dev, SUMOTime startTime, SUMOTime stopTime) {
487 132892 : if (dev.isNull()) {
488 29683 : reset();
489 29683 : return;
490 : }
491 103209 : const double t(STEPS2TIME(stopTime - startTime));
492 103209 : double occupancy = 0.;
493 : double speedSum = 0.;
494 : double lengthSum = 0.;
495 103209 : int contrib = 0;
496 : // to approximate the space mean speed
497 : double inverseSpeedSum = 0.;
498 1094069 : for (const VehicleData& vData : myVehicleDataCont) {
499 1977791 : const double timeOnDetDuringInterval = vData.leaveTimeM - MAX2(STEPS2TIME(startTime), vData.entryTimeM);
500 990860 : occupancy += MIN2(timeOnDetDuringInterval, t);
501 990860 : if (!vData.leftEarlyM) {
502 987847 : speedSum += vData.speedM;
503 : assert(vData.speedM > 0.);
504 987847 : inverseSpeedSum += 1. / vData.speedM;
505 987847 : lengthSum += vData.lengthM;
506 987847 : contrib++;
507 : }
508 : }
509 103209 : if (MSGlobals::gUseMesoSim) {
510 168 : contrib = myEnteredVehicleNumber;
511 : }
512 103209 : const double flow = (double)contrib / t * 3600.;
513 107380 : for (std::map< SUMOTrafficObject*, double >::const_iterator i = myVehiclesOnDet.begin(); i != myVehiclesOnDet.end(); ++i) {
514 8261 : occupancy += STEPS2TIME(stopTime) - MAX2(STEPS2TIME(startTime), i->second);
515 : }
516 103209 : occupancy *= 100. / t;
517 103209 : const double meanSpeed = contrib != 0 ? speedSum / (double)contrib : -1;
518 103209 : const double harmonicMeanSpeed = contrib != 0 ? (double)contrib / inverseSpeedSum : -1;
519 103209 : const double meanLength = contrib != 0 ? lengthSum / (double)contrib : -1;
520 206418 : dev.openTag(SUMO_TAG_INTERVAL).writeTime(SUMO_ATTR_BEGIN, startTime).writeTime(SUMO_ATTR_END, stopTime);
521 103209 : dev.writeAttr(SUMO_ATTR_ID, StringUtils::escapeXML(getID())).writeAttr("nVehContrib", contrib);
522 103209 : dev.writeAttr("flow", flow).writeAttr("occupancy", occupancy).writeAttr("speed", meanSpeed).writeAttr("harmonicMeanSpeed", harmonicMeanSpeed);
523 103209 : dev.writeAttr("length", meanLength).writeAttr("nVehEntered", myEnteredVehicleNumber).closeTag();
524 103209 : reset();
525 : }
526 :
527 :
528 : void
529 27632012 : MSInductLoop::detectorUpdate(const SUMOTime /* step */) {
530 27632012 : if (myDetectPersons == (int)PersonMode::NONE) {
531 : return;
532 : }
533 78492 : if (myLane->hasPedestrians()) {
534 202544 : for (MSTransportable* p : myLane->getEdge().getPersons()) {
535 183796 : if (p->getLane() != myLane || !vehicleApplies(*p)) {
536 28500 : continue;
537 : }
538 155296 : notifyMovePerson(p, p->getDirection(), p->getPositionOnLane());
539 : }
540 : }
541 : }
542 :
543 :
544 : void
545 158499 : MSInductLoop::notifyMovePerson(MSTransportable* p, int dir, double pos) {
546 158499 : if (personApplies(*p, dir)) {
547 114067 : const double newSpeed = p->getSpeed();
548 114067 : const double newPos = (dir == MSPModel::FORWARD
549 114067 : ? pos
550 : // position relative to detector
551 31992 : : myPosition - (pos - myPosition));
552 114067 : const double oldPos = newPos - SPEED2DIST(newSpeed);
553 114067 : if (oldPos - p->getVehicleType().getLength() <= myPosition) {
554 80986 : notifyMove(*p, oldPos, newPos, newSpeed);
555 : }
556 : }
557 158499 : }
558 :
559 :
560 : std::vector<MSInductLoop::VehicleData>
561 117546 : MSInductLoop::collectVehiclesOnDet(SUMOTime tMS, bool includeEarly, bool leaveTime, bool forOccupancy, bool lastInterval) const {
562 : #ifdef HAVE_FOX
563 117546 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
564 : #endif
565 117546 : const double t = STEPS2TIME(tMS);
566 : std::vector<VehicleData> ret;
567 1194903 : for (const VehicleData& i : myVehicleDataCont) {
568 1077357 : if ((includeEarly || !i.leftEarlyM) && (!lastInterval || i.entryTimeM < t)) {
569 1074531 : if (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)) {
570 5682 : ret.push_back(i);
571 : }
572 : }
573 : }
574 710997 : for (const VehicleData& i : myLastVehicleDataCont) {
575 593451 : if (includeEarly || !i.leftEarlyM) {
576 593451 : if ((!lastInterval && (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)))
577 6120 : || (lastInterval && i.leaveTimeM <= t + STEPS2TIME(myLastIntervalEnd - myLastIntervalBegin))) { // TODO: check duration of last interval
578 6529 : ret.push_back(i);
579 : }
580 : }
581 : }
582 127642 : for (const auto& i : myVehiclesOnDet) {
583 10096 : if ((!lastInterval && (i.second >= t || leaveTime || forOccupancy))
584 72 : || (lastInterval && i.second < t && t - i.second < STEPS2TIME(DELTA_T))) { // no need to check leave time, they are still on the detector
585 10018 : SUMOTrafficObject* const v = i.first;
586 10018 : VehicleData d(*v, i.second, HAS_NOT_LEFT_DETECTOR, false);
587 10018 : d.speedM = v->getSpeed();
588 10018 : ret.push_back(d);
589 : }
590 : }
591 117546 : return ret;
592 0 : }
593 :
594 :
595 1193923 : MSInductLoop::VehicleData::VehicleData(const SUMOTrafficObject& v, double entryTimestep,
596 1193923 : double leaveTimestep, const bool leftEarly, const double detLength)
597 1193923 : : idM(v.getID()), lengthM(v.getVehicleType().getLength()), entryTimeM(entryTimestep), leaveTimeM(leaveTimestep),
598 1203941 : speedM((v.getVehicleType().getLength() + detLength) / MAX2(leaveTimestep - entryTimestep, NUMERICAL_EPS)), typeIDM(v.getVehicleType().getID()),
599 1193923 : leftEarlyM(leftEarly) {}
600 :
601 :
602 : void
603 16 : MSInductLoop::clearState(SUMOTime time) {
604 16 : myLastLeaveTime = STEPS2TIME(time);
605 16 : myEnteredVehicleNumber = 0;
606 16 : myLastVehicleDataCont.clear();
607 16 : myVehicleDataCont.clear();
608 : myVehiclesOnDet.clear();
609 16 : }
610 :
611 : /****************************************************************************/
|