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