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 29857 : 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 29857 : const bool needLocking) :
71 : MSMoveReminder(id, lane),
72 : MSDetectorFileOutput(id, vTypes, nextEdges, detectPersons),
73 29853 : myName(name),
74 29853 : myPosition(positionInMeters),
75 29853 : myEndPosition(myPosition + length),
76 29853 : 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 29853 : myLastLeaveTime(-3600),
79 29853 : myOverrideTime(-1),
80 29853 : myOverrideEntryTime(-1),
81 : myVehicleDataCont(),
82 : myVehiclesOnDet(),
83 29853 : myLastIntervalEnd(-1),
84 29857 : mySegment(nullptr) {
85 : assert(length >= 0);
86 : assert(myPosition >= 0 && myEndPosition <= myLane->getLength());
87 29853 : reset();
88 29853 : if (MSGlobals::gUseMesoSim) {
89 : // used by actuated tls
90 1524 : mySegment = MSGlobals::gMesoNet->getSegmentForEdge(lane->getEdge(), myPosition);
91 1524 : mySegment->addDetector(this);
92 1524 : mySegmentPos = myPosition - mySegment->getIndex() * mySegment->getLength();
93 : }
94 29857 : }
95 :
96 :
97 57296 : MSInductLoop::~MSInductLoop() {
98 87027 : }
99 :
100 :
101 : void
102 162230 : MSInductLoop::reset() {
103 : #ifdef HAVE_FOX
104 162230 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
105 : #endif
106 162230 : myEnteredVehicleNumber = 0;
107 162230 : myLastVehicleDataCont = myVehicleDataCont;
108 : myVehicleDataCont.clear();
109 162230 : myLastIntervalBegin = myLastIntervalEnd;
110 162230 : myLastIntervalEnd = SIMSTEP;
111 162230 : }
112 :
113 :
114 : bool
115 1381540 : MSInductLoop::notifyEnter(SUMOTrafficObject& veh, Notification reason, const MSLane* /* enteredLane */) {
116 : // vehicles must be kept if the "inductionloop" wants to detect passeengers
117 1381540 : if (!vehicleApplies(veh) && (veh.isPerson() || myDetectPersons <= (int)PersonMode::WALK)) {
118 : return false;
119 : }
120 1376788 : if (MSGlobals::gUseMesoSim) {
121 168906 : MEVehicle* mesoveh = dynamic_cast<MEVehicle*>(&veh);
122 : assert(mesoveh != nullptr);
123 : const MESegment* seg = mesoveh->getSegment();
124 168906 : if (seg != nullptr && (seg->numQueues() == 1 || mesoveh->getQueIndex() == myLane->getIndex())) {
125 146814 : const SUMOTime onSegTime = mesoveh->getEventTime() - mesoveh->getLastEntryTime();
126 : // extrapolate movement
127 146814 : 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 146814 : myNextMesoLeaveTimes.push(exLeaveTime);
130 : }
131 168906 : return false;
132 : }
133 1207882 : if (reason != NOTIFICATION_JUNCTION) { // the junction case is handled in notifyMove
134 238368 : if (veh.getBackPositionOnLane(myLane) >= myPosition) {
135 : return false;
136 : }
137 235586 : if (veh.getPositionOnLane() >= myPosition) {
138 : #ifdef HAVE_FOX
139 1253 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
140 : #endif
141 1253 : myVehiclesOnDet[&veh] = SIMTIME;
142 1253 : myEnteredVehicleNumber++;
143 : }
144 : }
145 : return true;
146 : }
147 :
148 :
149 : bool
150 23177782 : MSInductLoop::notifyMove(SUMOTrafficObject& veh, double oldPos,
151 : double newPos, double newSpeed) {
152 23177782 : if (newPos < myPosition) {
153 : // detector not reached yet
154 : return true;
155 : }
156 2748847 : 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 2748809 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
166 : #endif
167 2748809 : const double oldSpeed = veh.getPreviousSpeed();
168 2748809 : if (newPos >= myPosition && oldPos < myPosition) {
169 : // entered the detector by move
170 1175604 : const double timeBeforeEnter = MSCFModel::passingTime(oldPos, myPosition, newPos, oldSpeed, newSpeed);
171 1175604 : myVehiclesOnDet[&veh] = SIMTIME + timeBeforeEnter;
172 1175604 : 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 2748809 : double oldBackPos = oldPos - veh.getVehicleType().getLength();
180 2748809 : double newBackPos = newPos - veh.getVehicleType().getLength();
181 2748809 : 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 1173228 : if (oldBackPos <= myEndPosition) {
186 : const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
187 1173228 : if (it != myVehiclesOnDet.end()) {
188 1173228 : const double entryTime = it->second;
189 1173228 : const double leaveTime = SIMTIME + MSCFModel::passingTime(oldBackPos, myEndPosition, newBackPos, oldSpeed, newSpeed);
190 : myVehiclesOnDet.erase(it);
191 : assert(entryTime <= leaveTime);
192 1173228 : myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, false, myEndPosition - myPosition));
193 1173228 : 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 0 : 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 1173228 : return false;
217 : }
218 : // vehicle stays on the detector
219 : return true;
220 : }
221 :
222 :
223 : bool
224 859083 : MSInductLoop::notifyLeave(SUMOTrafficObject& veh, double lastPos, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
225 859083 : if (veh.isPerson() && myDetectPersons != (int)PersonMode::NONE) {
226 3204 : const int lastDir = lastPos < 0 ? MSPModel::BACKWARD : MSPModel::FORWARD;
227 3204 : notifyMovePerson(dynamic_cast<MSTransportable*>(&veh), lastDir, lastPos);
228 : }
229 859083 : if (reason != MSMoveReminder::NOTIFICATION_JUNCTION || (veh.isPerson() && myDetectPersons != (int)PersonMode::NONE)) {
230 : #ifdef HAVE_FOX
231 32334 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
232 : #endif
233 : const std::map<SUMOTrafficObject*, double>::iterator it = myVehiclesOnDet.find(&veh);
234 32334 : if (it != myVehiclesOnDet.end()) {
235 3614 : const double entryTime = it->second;
236 3614 : const double leaveTime = SIMTIME + TS;
237 : myVehiclesOnDet.erase(it);
238 3614 : myVehicleDataCont.push_back(VehicleData(veh, entryTime, leaveTime, true));
239 3614 : myLastLeaveTime = leaveTime;
240 : }
241 : return false;
242 : }
243 : return true;
244 : }
245 :
246 :
247 : double
248 146 : MSInductLoop::getSpeed(const int offset) const {
249 146 : const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
250 175 : return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, speedSum) / (double) d.size();
251 146 : }
252 :
253 :
254 : double
255 146 : MSInductLoop::getVehicleLength(const int offset) const {
256 146 : const std::vector<VehicleData>& d = collectVehiclesOnDet(SIMSTEP - offset);
257 175 : return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, lengthSum) / (double)d.size();
258 146 : }
259 :
260 :
261 : double
262 55946 : MSInductLoop::getOccupancy() const {
263 55946 : if (myOverrideTime >= 0) {
264 48 : return myOverrideTime < TS ? (TS - myOverrideTime) / TS * 100 : 0;
265 : }
266 55898 : const SUMOTime tbeg = SIMSTEP - DELTA_T;
267 : double occupancy = 0;
268 55898 : const double csecond = SIMTIME;
269 61783 : for (const VehicleData& i : collectVehiclesOnDet(tbeg, false, false, true)) {
270 5885 : const double leaveTime = i.leaveTimeM == HAS_NOT_LEFT_DETECTOR ? csecond : MIN2(i.leaveTimeM, csecond);
271 5885 : const double entryTime = MAX2(i.entryTimeM, STEPS2TIME(tbeg));
272 8697 : occupancy += MIN2(leaveTime - entryTime, TS);
273 55898 : }
274 55898 : return occupancy / TS * 100.;
275 : }
276 :
277 :
278 : double
279 3217 : MSInductLoop::getEnteredNumber(const int offset) const {
280 3217 : if (myOverrideTime >= 0) {
281 48 : return myOverrideTime < TS ? 1 : 0;
282 : }
283 3169 : return (double)collectVehiclesOnDet(SIMSTEP - offset, true, true).size();
284 : }
285 :
286 :
287 : std::vector<std::string>
288 970 : MSInductLoop::getVehicleIDs(const int offset) const {
289 : std::vector<std::string> ret;
290 1239 : for (const VehicleData& i : collectVehiclesOnDet(SIMSTEP - offset, true, true)) {
291 269 : ret.push_back(i.idM);
292 970 : }
293 970 : return ret;
294 0 : }
295 :
296 :
297 : double
298 1111606 : MSInductLoop::getTimeSinceLastDetection() const {
299 1111606 : if (myOverrideTime >= 0) {
300 : return myOverrideTime;
301 : }
302 1111558 : if (myVehiclesOnDet.size() != 0) {
303 : // detector is occupied
304 : return 0;
305 : }
306 1052712 : if (MSGlobals::gUseMesoSim) {
307 : //std::cout << SIMTIME << " det=" << getID() << " qBefore=" << myNextMesoLeaveTimes.size() << " last=" << STEPS2TIME(getLastDetectionTime()) << " qAfter=" << myNextMesoLeaveTimes.size() << " top=" << (myNextMesoLeaveTimes.empty() ? -1 : myNextMesoLeaveTimes.top()) << "\n";
308 439250 : return SIMTIME - STEPS2TIME(getLastDetectionTime());
309 : }
310 613462 : return SIMTIME - myLastLeaveTime;
311 : }
312 :
313 :
314 : void
315 16 : MSInductLoop::loadTimeSinceLastDetection(double time) {
316 16 : myLastLeaveTime = SIMTIME - time;
317 16 : }
318 :
319 :
320 : double
321 185150 : MSInductLoop::getOccupancyTime() const {
322 : #ifdef HAVE_FOX
323 185150 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
324 : #endif
325 185150 : if (myOverrideTime >= 0) {
326 0 : return SIMTIME - myOverrideEntryTime;
327 : }
328 185150 : if (myVehiclesOnDet.size() == 0) {
329 : // detector is unoccupied
330 : return 0;
331 : } else {
332 : double minEntry = std::numeric_limits<double>::max();
333 4530 : for (const auto& i : myVehiclesOnDet) {
334 2265 : minEntry = MIN2(i.second, minEntry);
335 : }
336 2265 : return SIMTIME - minEntry;
337 : }
338 : }
339 :
340 :
341 : double
342 1044 : MSInductLoop::getArrivalDelay() const {
343 : #ifdef HAVE_FOX
344 1044 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
345 : #endif
346 1044 : MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
347 : double result = -INVALID_DOUBLE;
348 1072 : for (const auto& item : collectVehiclesOnDet(SIMSTEP - DELTA_T)) {
349 28 : SUMOVehicle* v = vc.getVehicle(item.idM);
350 28 : if (v != nullptr) {
351 28 : MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(v);
352 28 : double ad = veh->getStopArrivalDelay();
353 28 : if (ad != INVALID_DOUBLE) {
354 : result = MAX2(result, ad);
355 : }
356 : }
357 1044 : }
358 1044 : return result;
359 : }
360 :
361 :
362 : SUMOTime
363 845212 : MSInductLoop::getLastDetectionTime() const {
364 845212 : if (myOverrideTime >= 0) {
365 0 : return SIMSTEP - TIME2STEPS(myOverrideTime);
366 : }
367 845212 : if (myVehiclesOnDet.size() != 0) {
368 12676 : return MSNet::getInstance()->getCurrentTimeStep();
369 : }
370 : if (MSGlobals::gUseMesoSim
371 568714 : && !myNextMesoLeaveTimes.empty()
372 1078344 : && myNextMesoLeaveTimes.top() < SIMSTEP) {
373 : // find the latest time that is already in the past
374 245494 : SUMOTime last = myNextMesoLeaveTimes.top();
375 245494 : if (myNextMesoLeaveTimes.size() > 1) {
376 : myNextMesoLeaveTimes.pop();
377 307412 : while (myNextMesoLeaveTimes.size() > 0 && myNextMesoLeaveTimes.top() < SIMSTEP) {
378 142706 : last = myNextMesoLeaveTimes.top();
379 : myNextMesoLeaveTimes.pop();
380 : }
381 164706 : myNextMesoLeaveTimes.push(last);
382 : }
383 245494 : const SUMOTime blockTime = mySegment->getQueueBlockTime(MIN2(mySegment->numQueues() - 1, myLane->getIndex()));
384 245494 : const SUMOTime last2 = blockTime - TIME2STEPS((mySegment->getLength() - mySegmentPos) / myLane->getSpeedLimit());
385 : //std::cout << SIMTIME << " det=" << getID() << " last=" << last << " last2=" << last2 << " block=" << blockTime << " times=" << myNextMesoLeaveTimes.size() << "\n";
386 245494 : return MAX2(last, last2);
387 : }
388 1019754 : return TIME2STEPS(myLastLeaveTime);
389 : }
390 :
391 :
392 : double
393 972 : MSInductLoop::getIntervalOccupancy(bool lastInterval) const {
394 : double occupancy = 0;
395 972 : const double csecond = lastInterval ? STEPS2TIME(myLastIntervalEnd) : SIMTIME;
396 972 : const double aggTime = csecond - STEPS2TIME(lastInterval ? myLastIntervalBegin : myLastIntervalEnd);
397 972 : if (aggTime == 0) {
398 : return 0;
399 : }
400 4040 : for (const VehicleData& i : collectVehiclesOnDet(myLastIntervalEnd, false, false, true, lastInterval)) {
401 3072 : const double leaveTime = i.leaveTimeM == HAS_NOT_LEFT_DETECTOR ? csecond : MIN2(i.leaveTimeM, csecond);
402 3072 : const double entryTime = MAX2(i.entryTimeM, STEPS2TIME(lastInterval ? myLastIntervalBegin : myLastIntervalEnd));
403 3072 : occupancy += MIN2(leaveTime - entryTime, aggTime);
404 968 : }
405 968 : return occupancy / aggTime * 100.;
406 : }
407 :
408 :
409 : double
410 972 : MSInductLoop::getIntervalMeanSpeed(bool lastInterval) const {
411 972 : const std::vector<VehicleData>& d = collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval);
412 1644 : return d.empty() ? -1. : std::accumulate(d.begin(), d.end(), 0.0, speedSum) / (double) d.size();
413 972 : }
414 :
415 :
416 : int
417 972 : MSInductLoop::getIntervalVehicleNumber(bool lastInterval) const {
418 972 : return (int)collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval).size();
419 : }
420 :
421 :
422 : std::vector<std::string>
423 972 : MSInductLoop::getIntervalVehicleIDs(bool lastInterval) const {
424 : std::vector<std::string> ret;
425 4044 : for (const VehicleData& i : collectVehiclesOnDet(myLastIntervalEnd, false, false, false, lastInterval)) {
426 3072 : ret.push_back(i.idM);
427 972 : }
428 972 : return ret;
429 0 : }
430 :
431 :
432 : void
433 20 : MSInductLoop::overrideTimeSinceDetection(double time) {
434 20 : myOverrideTime = time;
435 20 : if (time < 0) {
436 4 : myOverrideEntryTime = -1;
437 : } else {
438 16 : const double entryTime = MAX2(0.0, SIMTIME - time);
439 16 : if (myOverrideEntryTime >= 0) {
440 : // maintain earlier entry time to achive continous detection
441 12 : myOverrideEntryTime = MIN2(myOverrideEntryTime, entryTime);
442 : } else {
443 4 : myOverrideEntryTime = entryTime;
444 : }
445 : }
446 20 : }
447 :
448 : void
449 29849 : MSInductLoop::writeXMLDetectorProlog(OutputDevice& dev) const {
450 59698 : dev.writeXMLHeader("detector", "det_e1_file.xsd");
451 29849 : }
452 :
453 :
454 : void
455 132377 : MSInductLoop::writeXMLOutput(OutputDevice& dev, SUMOTime startTime, SUMOTime stopTime) {
456 132377 : if (dev.isNull()) {
457 29198 : reset();
458 29198 : return;
459 : }
460 103179 : const double t(STEPS2TIME(stopTime - startTime));
461 103179 : double occupancy = 0.;
462 : double speedSum = 0.;
463 : double lengthSum = 0.;
464 103179 : int contrib = 0;
465 : // to approximate the space mean speed
466 : double inverseSpeedSum = 0.;
467 1093703 : for (const VehicleData& vData : myVehicleDataCont) {
468 1977125 : const double timeOnDetDuringInterval = vData.leaveTimeM - MAX2(STEPS2TIME(startTime), vData.entryTimeM);
469 990524 : occupancy += MIN2(timeOnDetDuringInterval, t);
470 990524 : if (!vData.leftEarlyM) {
471 987507 : speedSum += vData.speedM;
472 : assert(vData.speedM > 0.);
473 987507 : inverseSpeedSum += 1. / vData.speedM;
474 987507 : lengthSum += vData.lengthM;
475 987507 : contrib++;
476 : }
477 : }
478 103179 : const double flow = (double)contrib / t * 3600.;
479 107344 : for (std::map< SUMOTrafficObject*, double >::const_iterator i = myVehiclesOnDet.begin(); i != myVehiclesOnDet.end(); ++i) {
480 8249 : occupancy += STEPS2TIME(stopTime) - MAX2(STEPS2TIME(startTime), i->second);
481 : }
482 103179 : occupancy *= 100. / t;
483 103179 : const double meanSpeed = contrib != 0 ? speedSum / (double)contrib : -1;
484 103179 : const double harmonicMeanSpeed = contrib != 0 ? (double)contrib / inverseSpeedSum : -1;
485 103179 : const double meanLength = contrib != 0 ? lengthSum / (double)contrib : -1;
486 206358 : dev.openTag(SUMO_TAG_INTERVAL).writeTime(SUMO_ATTR_BEGIN, startTime).writeTime(SUMO_ATTR_END, stopTime);
487 103179 : dev.writeAttr(SUMO_ATTR_ID, StringUtils::escapeXML(getID())).writeAttr("nVehContrib", contrib);
488 103179 : dev.writeAttr("flow", flow).writeAttr("occupancy", occupancy).writeAttr("speed", meanSpeed).writeAttr("harmonicMeanSpeed", harmonicMeanSpeed);
489 103179 : dev.writeAttr("length", meanLength).writeAttr("nVehEntered", myEnteredVehicleNumber).closeTag();
490 103179 : reset();
491 : }
492 :
493 :
494 : void
495 27497251 : MSInductLoop::detectorUpdate(const SUMOTime /* step */) {
496 27497251 : if (myDetectPersons == (int)PersonMode::NONE) {
497 : return;
498 : }
499 78592 : if (myLane->hasPedestrians()) {
500 202640 : for (MSTransportable* p : myLane->getEdge().getPersons()) {
501 183844 : if (p->getLane() != myLane || !vehicleApplies(*p)) {
502 28504 : continue;
503 : }
504 155340 : notifyMovePerson(p, p->getDirection(), p->getPositionOnLane());
505 : }
506 : }
507 : }
508 :
509 :
510 : void
511 158544 : MSInductLoop::notifyMovePerson(MSTransportable* p, int dir, double pos) {
512 158544 : if (personApplies(*p, dir)) {
513 114112 : const double newSpeed = p->getSpeed();
514 114112 : const double newPos = (dir == MSPModel::FORWARD
515 114112 : ? pos
516 : // position relative to detector
517 31992 : : myPosition - (pos - myPosition));
518 114112 : const double oldPos = newPos - SPEED2DIST(newSpeed);
519 114112 : if (oldPos - p->getVehicleType().getLength() <= myPosition) {
520 80988 : notifyMove(*p, oldPos, newPos, newSpeed);
521 : }
522 : }
523 158544 : }
524 :
525 :
526 : std::vector<MSInductLoop::VehicleData>
527 117763 : MSInductLoop::collectVehiclesOnDet(SUMOTime tMS, bool includeEarly, bool leaveTime, bool forOccupancy, bool lastInterval) const {
528 : #ifdef HAVE_FOX
529 117763 : ScopedLocker<> lock(myNotificationMutex, myNeedLock);
530 : #endif
531 117763 : const double t = STEPS2TIME(tMS);
532 : std::vector<VehicleData> ret;
533 1197167 : for (const VehicleData& i : myVehicleDataCont) {
534 1079404 : if ((includeEarly || !i.leftEarlyM) && (!lastInterval || i.entryTimeM < t)) {
535 1075636 : if (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)) {
536 6578 : ret.push_back(i);
537 : }
538 : }
539 : }
540 715294 : for (const VehicleData& i : myLastVehicleDataCont) {
541 597531 : if (includeEarly || !i.leftEarlyM) {
542 597531 : if ((!lastInterval && (i.entryTimeM >= t || (leaveTime && i.leaveTimeM >= t)))
543 8160 : || (lastInterval && i.leaveTimeM <= t + STEPS2TIME(myLastIntervalEnd - myLastIntervalBegin))) { // TODO: check duration of last interval
544 8689 : ret.push_back(i);
545 : }
546 : }
547 : }
548 128116 : for (const auto& i : myVehiclesOnDet) {
549 10353 : if ((!lastInterval && (i.second >= t || leaveTime || forOccupancy))
550 96 : || (lastInterval && i.second < t && t - i.second < STEPS2TIME(DELTA_T))) { // no need to check leave time, they are still on the detector
551 10249 : SUMOTrafficObject* const v = i.first;
552 10249 : VehicleData d(*v, i.second, HAS_NOT_LEFT_DETECTOR, false);
553 10249 : d.speedM = v->getSpeed();
554 10249 : ret.push_back(d);
555 : }
556 : }
557 117763 : return ret;
558 0 : }
559 :
560 :
561 1187091 : MSInductLoop::VehicleData::VehicleData(const SUMOTrafficObject& v, double entryTimestep,
562 1187091 : double leaveTimestep, const bool leftEarly, const double detLength)
563 1187091 : : idM(v.getID()), lengthM(v.getVehicleType().getLength()), entryTimeM(entryTimestep), leaveTimeM(leaveTimestep),
564 1197340 : speedM((v.getVehicleType().getLength() + detLength) / MAX2(leaveTimestep - entryTimestep, NUMERICAL_EPS)), typeIDM(v.getVehicleType().getID()),
565 1187091 : leftEarlyM(leftEarly) {}
566 :
567 :
568 : void
569 16 : MSInductLoop::clearState(SUMOTime time) {
570 16 : myLastLeaveTime = STEPS2TIME(time);
571 16 : myEnteredVehicleNumber = 0;
572 16 : myLastVehicleDataCont.clear();
573 16 : myVehicleDataCont.clear();
574 : myVehiclesOnDet.clear();
575 16 : }
576 :
577 : /****************************************************************************/
|