Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2011-2024 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 NBLoadedSUMOTLDef.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Michael Behrisch
17 : /// @author Jakob Erdmann
18 : /// @date Mar 2011
19 : ///
20 : // A complete traffic light logic loaded from a sumo-net. (opted to reimplement
21 : // since NBLoadedTLDef is quite vissim specific)
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <vector>
26 : #include <set>
27 : #include <cassert>
28 : #include <iterator>
29 : #include <utils/common/MsgHandler.h>
30 : #include <utils/common/ToString.h>
31 : #include <utils/options/OptionsCont.h>
32 : #include "NBTrafficLightLogic.h"
33 : #include "NBOwnTLDef.h"
34 : #include "NBTrafficLightDefinition.h"
35 : #include "NBLoadedSUMOTLDef.h"
36 : #include "NBNetBuilder.h"
37 : #include "NBOwnTLDef.h"
38 : #include "NBNode.h"
39 :
40 : //#define DEBUG_RECONSTRUCTION
41 :
42 : // ===========================================================================
43 : // method definitions
44 : // ===========================================================================
45 :
46 549 : NBLoadedSUMOTLDef::NBLoadedSUMOTLDef(const std::string& id, const std::string& programID,
47 549 : SUMOTime offset, TrafficLightType type) :
48 : NBTrafficLightDefinition(id, programID, offset, type),
49 549 : myTLLogic(nullptr),
50 549 : myReconstructAddedConnections(false),
51 549 : myReconstructRemovedConnections(false),
52 549 : myPhasesLoaded(false) {
53 549 : myTLLogic = new NBTrafficLightLogic(id, programID, 0, offset, type);
54 549 : }
55 :
56 :
57 13 : NBLoadedSUMOTLDef::NBLoadedSUMOTLDef(const NBTrafficLightDefinition& def, const NBTrafficLightLogic& logic) :
58 : // allow for adding a new program for the same def: take the offset and programID from the new logic
59 : NBTrafficLightDefinition(def.getID(), logic.getProgramID(), logic.getOffset(), def.getType()),
60 13 : myTLLogic(new NBTrafficLightLogic(logic)),
61 13 : myReconstructAddedConnections(false),
62 13 : myReconstructRemovedConnections(false),
63 26 : myPhasesLoaded(false) {
64 : assert(def.getType() == logic.getType());
65 13 : myControlledLinks = def.getControlledLinks();
66 13 : myControlledNodes = def.getNodes();
67 13 : const NBLoadedSUMOTLDef* sumoDef = dynamic_cast<const NBLoadedSUMOTLDef*>(&def);
68 13 : updateParameters(def.getParametersMap());
69 13 : if (sumoDef != nullptr) {
70 11 : myReconstructAddedConnections = sumoDef->myReconstructAddedConnections;
71 11 : myReconstructRemovedConnections = sumoDef->myReconstructRemovedConnections;
72 : }
73 13 : }
74 :
75 :
76 1124 : NBLoadedSUMOTLDef::~NBLoadedSUMOTLDef() {
77 562 : delete myTLLogic;
78 1124 : }
79 :
80 :
81 : NBTrafficLightLogic*
82 622 : NBLoadedSUMOTLDef::myCompute(int brakingTimeSeconds) {
83 : // @todo what to do with those parameters?
84 : UNUSED_PARAMETER(brakingTimeSeconds);
85 622 : reconstructLogic();
86 622 : myTLLogic->closeBuilding(false);
87 622 : patchIfCrossingsAdded();
88 622 : myTLLogic->closeBuilding();
89 622 : return new NBTrafficLightLogic(myTLLogic);
90 : }
91 :
92 :
93 : void
94 5332 : NBLoadedSUMOTLDef::addConnection(NBEdge* from, NBEdge* to, int fromLane, int toLane, int linkIndex, int linkIndex2, bool reconstruct) {
95 : assert(myTLLogic->getNumLinks() > 0); // logic should be loaded by now
96 5332 : if (linkIndex >= myTLLogic->getNumLinks()) {
97 0 : throw ProcessError("Invalid linkIndex " + toString(linkIndex) + " in connection from edge '" + from->getID() +
98 0 : "' to edge '" + to->getID() + "' for traffic light '" + getID() +
99 0 : "' with " + toString(myTLLogic->getNumLinks()) + " links.");
100 : }
101 5332 : if (linkIndex2 >= myTLLogic->getNumLinks()) {
102 0 : throw ProcessError("Invalid linkIndex2 " + toString(linkIndex2) + " in connection from edge '" + from->getID() +
103 0 : "' to edge '" + to->getID() + "' for traffic light '" + getID() +
104 0 : "' with " + toString(myTLLogic->getNumLinks()) + " links.");
105 : }
106 5332 : NBConnection conn(from, fromLane, to, toLane, linkIndex, linkIndex2);
107 : // avoid duplicates
108 5332 : auto newEnd = remove_if(myControlledLinks.begin(), myControlledLinks.end(), connection_equal(conn));
109 : // remove_if does not remove, only re-order
110 5332 : myControlledLinks.erase(newEnd, myControlledLinks.end());
111 5332 : myControlledLinks.push_back(conn);
112 5332 : addNode(from->getToNode());
113 5332 : addNode(to->getFromNode());
114 : // added connections are definitely controlled. make sure none are removed because they lie within the tl
115 : // myControlledInnerEdges.insert(from->getID()); // @todo recheck: this appears to be obsolete
116 : // set this information now so that it can be used while loading diffs
117 5332 : from->setControllingTLInformation(conn, getID());
118 5332 : myReconstructAddedConnections |= reconstruct;
119 5332 : }
120 :
121 : void
122 46 : NBLoadedSUMOTLDef::setID(const std::string& newID) {
123 : Named::setID(newID);
124 46 : myTLLogic->setID(newID);
125 46 : }
126 :
127 : void
128 0 : NBLoadedSUMOTLDef::setProgramID(const std::string& programID) {
129 : NBTrafficLightDefinition::setProgramID(programID);
130 0 : myTLLogic->setProgramID(programID);
131 0 : }
132 :
133 :
134 : void
135 567 : NBLoadedSUMOTLDef::setTLControllingInformation() const {
136 567 : if (myReconstructAddedConnections) {
137 5 : NBOwnTLDef dummy(DummyID, myControlledNodes, 0, getType());
138 5 : dummy.setParticipantsInformation();
139 5 : dummy.setTLControllingInformation();
140 10 : for (NBNode* const n : myControlledNodes) {
141 5 : n->removeTrafficLight(&dummy);
142 : }
143 5 : }
144 567 : if (myReconstructRemovedConnections) {
145 : return; // will be called again in reconstructLogic()
146 : }
147 : // if nodes have been removed our links may have been invalidated as well
148 : // since no logic will be built anyway there is no reason to inform any edges
149 539 : if (amInvalid()) {
150 : return;
151 : }
152 : // set the information about the link's positions within the tl into the
153 : // edges the links are starting at, respectively
154 5901 : for (const NBConnection& c : myControlledLinks) {
155 5370 : if (c.getTLIndex() >= myTLLogic->getNumLinks()) {
156 2 : throw ProcessError("Invalid linkIndex " + toString(c.getTLIndex()) + " for traffic light '" + getID() +
157 3 : "' with " + toString(myTLLogic->getNumLinks()) + " links.");
158 : }
159 5369 : NBEdge* edge = c.getFrom();
160 5369 : if (edge != nullptr && edge->getNumLanes() > c.getFromLane()) {
161 : // logic may have yet to be reconstructed
162 5369 : edge->setControllingTLInformation(c, getID());
163 : }
164 : }
165 : }
166 :
167 :
168 : void
169 0 : NBLoadedSUMOTLDef::remapRemoved(NBEdge*, const EdgeVector&, const EdgeVector&) {}
170 :
171 :
172 : void
173 75 : NBLoadedSUMOTLDef::replaceRemoved(NBEdge* removed, int removedLane, NBEdge* by, int byLane, bool incoming) {
174 75 : if (by == nullptr) {
175 45 : myReconstructRemovedConnections = true;
176 : }
177 963 : for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); ++it) {
178 888 : if (incoming) {
179 591 : (*it).replaceFrom(removed, removedLane, by, byLane);
180 : } else {
181 297 : (*it).replaceTo(removed, removedLane, by, byLane);
182 : }
183 : }
184 75 : }
185 :
186 :
187 : void
188 3695 : NBLoadedSUMOTLDef::addPhase(const SUMOTime duration, const std::string& state, const SUMOTime minDur, const SUMOTime maxDur,
189 : const SUMOTime earliestEnd, const SUMOTime latestEnd, const SUMOTime vehExt, const SUMOTime yellow,
190 : const SUMOTime red, const std::vector<int>& next, const std::string& name) {
191 3695 : myTLLogic->addStep(duration, state, minDur, maxDur, earliestEnd, latestEnd, vehExt, yellow, red, name, next);
192 3694 : }
193 :
194 :
195 : bool
196 41725 : NBLoadedSUMOTLDef::amInvalid() const {
197 41725 : if (myControlledLinks.size() == 0) {
198 : return true;
199 : }
200 41720 : if (myIncomingEdges.size() == 0) {
201 : return true;
202 : }
203 41718 : if (myReconstructRemovedConnections) {
204 : // check whether at least one connection is valid
205 20 : for (const NBConnection& con : myControlledLinks) {
206 19 : if (isValid(con)) {
207 : return false;
208 : }
209 : }
210 : // all invalid
211 : return true;
212 : }
213 : return false;
214 : }
215 :
216 :
217 : void
218 145 : NBLoadedSUMOTLDef::removeConnection(const NBConnection& conn, bool reconstruct) {
219 1377 : for (auto it = myControlledLinks.begin(); it != myControlledLinks.end();) {
220 1625 : if ((it->getFrom() == conn.getFrom() &&
221 518 : it->getTo() == conn.getTo() &&
222 214 : it->getFromLane() == conn.getFromLane() &&
223 89 : it->getToLane() == conn.getToLane())
224 1543 : || (it->getTLIndex() == conn.getTLIndex() &&
225 18 : conn.getTLIndex() != conn.InvalidTlIndex &&
226 14 : (it->getFrom() == nullptr || it->getTo() == nullptr))) {
227 91 : if (reconstruct) {
228 60 : myReconstructRemovedConnections = true;
229 : it++;
230 : } else {
231 31 : it = myControlledLinks.erase(it);
232 : }
233 : } else {
234 : it++;
235 : }
236 : }
237 145 : }
238 :
239 :
240 : void
241 6 : NBLoadedSUMOTLDef::setOffset(SUMOTime offset) {
242 6 : myOffset = offset;
243 6 : myTLLogic->setOffset(offset);
244 6 : }
245 :
246 :
247 : void
248 7 : NBLoadedSUMOTLDef::setType(TrafficLightType type) {
249 7 : myType = type;
250 7 : myTLLogic->setType(type);
251 7 : }
252 :
253 :
254 : void
255 543 : NBLoadedSUMOTLDef::collectEdges() {
256 543 : if (myControlledLinks.size() == 0) {
257 15 : NBTrafficLightDefinition::collectEdges();
258 : }
259 : myIncomingEdges.clear();
260 : EdgeVector myOutgoing;
261 : // collect the edges from the participating nodes
262 1215 : for (std::vector<NBNode*>::iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
263 672 : const EdgeVector& incoming = (*i)->getIncomingEdges();
264 672 : copy(incoming.begin(), incoming.end(), back_inserter(myIncomingEdges));
265 672 : const EdgeVector& outgoing = (*i)->getOutgoingEdges();
266 : copy(outgoing.begin(), outgoing.end(), back_inserter(myOutgoing));
267 : }
268 : // check which of the edges are completely within the junction
269 : // and which are uncontrolled as well (we already know myControlledLinks)
270 2280 : for (EdgeVector::iterator j = myIncomingEdges.begin(); j != myIncomingEdges.end();) {
271 1737 : NBEdge* edge = *j;
272 : edge->setInsideTLS(false); // reset
273 : // an edge lies within the logic if it is outgoing as well as incoming
274 1737 : EdgeVector::iterator k = std::find(myOutgoing.begin(), myOutgoing.end(), edge);
275 1737 : if (k != myOutgoing.end()) {
276 122 : if (myControlledInnerEdges.count(edge->getID()) == 0) {
277 : bool controlled = false;
278 798 : for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
279 794 : if ((*it).getFrom() == edge) {
280 : controlled = true;
281 : break;
282 : }
283 : }
284 122 : if (controlled) {
285 118 : myControlledInnerEdges.insert(edge->getID());
286 : } else {
287 4 : myEdgesWithin.push_back(edge);
288 : edge->setInsideTLS(true);
289 : ++j; //j = myIncomingEdges.erase(j);
290 4 : continue;
291 : }
292 : }
293 : }
294 : ++j;
295 : }
296 543 : }
297 :
298 :
299 : void
300 543 : NBLoadedSUMOTLDef::collectLinks() {
301 543 : if (myControlledLinks.size() == 0) {
302 : // maybe we only loaded a different program for a default traffic light.
303 : // Try to build links now.
304 15 : collectAllLinks(myControlledLinks);
305 : }
306 543 : }
307 :
308 :
309 : /// @brief patches signal plans by modifying lane indices
310 : void
311 16 : NBLoadedSUMOTLDef::shiftTLConnectionLaneIndex(NBEdge* edge, int offset, int threshold) {
312 : // avoid shifting twice if the edge is incoming and outgoing to a joined TLS
313 : if (myShifted.count(edge) == 0) {
314 : /// XXX what if an edge should really be shifted twice?
315 : myShifted.insert(edge);
316 224 : for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
317 208 : (*it).shiftLaneIndex(edge, offset, threshold);
318 : }
319 : }
320 16 : }
321 :
322 : void
323 622 : NBLoadedSUMOTLDef::patchIfCrossingsAdded() {
324 622 : const int size = myTLLogic->getNumLinks();
325 : int noLinksAll = 0;
326 7484 : for (NBConnectionVector::const_iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
327 : const NBConnection& c = *it;
328 6862 : if (c.getTLIndex() != NBConnection::InvalidTlIndex) {
329 6862 : noLinksAll = MAX2(noLinksAll, (int)c.getTLIndex() + 1);
330 : }
331 : }
332 : const int numNormalLinks = noLinksAll;
333 : int oldCrossings = 0;
334 : // collect crossings
335 : bool customIndex = false;
336 : std::vector<NBNode::Crossing*> crossings;
337 1395 : for (std::vector<NBNode*>::iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
338 773 : const std::vector<NBNode::Crossing*>& c = (*i)->getCrossings();
339 : // set tl indices for crossings
340 773 : customIndex |= (*i)->setCrossingTLIndices(getID(), noLinksAll);
341 : copy(c.begin(), c.end(), std::back_inserter(crossings));
342 773 : noLinksAll += (int)c.size();
343 773 : oldCrossings += (*i)->numCrossingsFromSumoNet();
344 773 : }
345 622 : if ((int)crossings.size() != oldCrossings) {
346 15 : std::vector<NBTrafficLightLogic::PhaseDefinition> phases = myTLLogic->getPhases();
347 : // do not rebuilt crossing states there are custom indices and the state string is long enough
348 15 : if (phases.size() > 0 && (
349 15 : (int)(phases.front().state.size()) < noLinksAll ||
350 4 : ((int)(phases.front().state.size()) > noLinksAll && !customIndex))) {
351 : // collect edges
352 6 : EdgeVector fromEdges(size, (NBEdge*)nullptr);
353 6 : EdgeVector toEdges(size, (NBEdge*)nullptr);
354 6 : std::vector<int> fromLanes(size, 0);
355 6 : collectEdgeVectors(fromEdges, toEdges, fromLanes);
356 : const std::string crossingDefaultState(crossings.size(), 'r');
357 :
358 : // rebuild the logic (see NBOwnTLDef.cpp::myCompute)
359 6 : NBTrafficLightLogic* newLogic = new NBTrafficLightLogic(getID(), getProgramID(), 0, myOffset, myType);
360 6 : SUMOTime brakingTime = TIME2STEPS(computeBrakingTime(OptionsCont::getOptions().getFloat("tls.yellow.min-decel")));
361 : //std::cout << "patchIfCrossingsAdded for " << getID() << " numPhases=" << phases.size() << "\n";
362 50 : for (const auto& phase : phases) {
363 88 : const std::string state = phase.state.substr(0, numNormalLinks) + crossingDefaultState;
364 88 : NBOwnTLDef::addPedestrianPhases(newLogic, phase.duration, phase.minDur, phase.maxDur, phase.earliestEnd, phase.latestEnd,
365 : state, crossings, fromEdges, toEdges);
366 : }
367 6 : NBOwnTLDef::addPedestrianScramble(newLogic, noLinksAll, TIME2STEPS(10), brakingTime, crossings, fromEdges, toEdges);
368 :
369 6 : delete myTLLogic;
370 6 : myTLLogic = newLogic;
371 15 : } else if (phases.size() == 0) {
372 0 : WRITE_WARNINGF(TL("Could not patch tlLogic '%' for changed crossings"), getID());
373 : }
374 15 : }
375 622 : }
376 :
377 :
378 : void
379 6 : NBLoadedSUMOTLDef::collectEdgeVectors(EdgeVector& fromEdges, EdgeVector& toEdges, std::vector<int>& fromLanes) const {
380 : assert(fromEdges.size() > 0);
381 : assert(fromEdges.size() == toEdges.size());
382 6 : const int size = (int)fromEdges.size();
383 :
384 72 : for (NBConnectionVector::const_iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
385 : const NBConnection& c = *it;
386 66 : if (c.getTLIndex() != NBConnection::InvalidTlIndex) {
387 66 : if (c.getTLIndex() >= size) {
388 0 : throw ProcessError("Invalid linkIndex " + toString(c.getTLIndex()) + " for traffic light '" + getID() +
389 0 : "' with " + toString(size) + " links.");
390 : }
391 66 : fromEdges[c.getTLIndex()] = c.getFrom();
392 66 : toEdges[c.getTLIndex()] = c.getTo();
393 66 : fromLanes[c.getTLIndex()] = c.getFromLane();
394 : }
395 : }
396 6 : }
397 :
398 :
399 : void
400 500 : NBLoadedSUMOTLDef::initNeedsContRelation() const {
401 500 : if (!amInvalid() && !myNeedsContRelationReady) {
402 : myNeedsContRelation.clear();
403 : myRightOnRedConflicts.clear();
404 500 : if (myType == TrafficLightType::NEMA) {
405 3 : NBTrafficLightDefinition::initNeedsContRelation();
406 3 : NBTrafficLightDefinition::initRightOnRedConflicts();
407 : } else {
408 497 : const bool controlledWithin = !OptionsCont::getOptions().getBool("tls.uncontrolled-within");
409 497 : const std::vector<NBTrafficLightLogic::PhaseDefinition> phases = myTLLogic->getPhases();
410 3164 : for (std::vector<NBTrafficLightLogic::PhaseDefinition>::const_iterator it = phases.begin(); it != phases.end(); it++) {
411 : const std::string state = (*it).state;
412 34790 : for (NBConnectionVector::const_iterator it1 = myControlledLinks.begin(); it1 != myControlledLinks.end(); it1++) {
413 : const NBConnection& c1 = *it1;
414 : const int i1 = c1.getTLIndex();
415 32123 : if (i1 == NBConnection::InvalidTlIndex || (state[i1] != 'g' && state[i1] != 's') || c1.getFrom() == nullptr || c1.getTo() == nullptr) {
416 : continue;
417 : }
418 40235 : for (NBConnectionVector::const_iterator it2 = myControlledLinks.begin(); it2 != myControlledLinks.end(); it2++) {
419 : const NBConnection& c2 = *it2;
420 : const int i2 = c2.getTLIndex();
421 : if (i2 != NBConnection::InvalidTlIndex
422 38145 : && i2 != i1
423 35879 : && (state[i2] == 'G' || state[i2] == 'g')
424 51760 : && c2.getFrom() != nullptr && c2.getTo() != nullptr) {
425 40845 : const bool rightTurnConflict = NBNode::rightTurnConflict(
426 27230 : c1.getFrom(), c1.getTo(), c1.getFromLane(), c2.getFrom(), c2.getTo(), c2.getFromLane());
427 13615 : const bool forbidden = forbids(c2.getFrom(), c2.getTo(), c1.getFrom(), c1.getTo(), true, controlledWithin);
428 13615 : const bool isFoes = foes(c2.getFrom(), c2.getTo(), c1.getFrom(), c1.getTo()) && !c2.getFrom()->isTurningDirectionAt(c2.getTo());
429 13615 : if (forbidden || rightTurnConflict) {
430 5588 : myNeedsContRelation.insert(StreamPair(c1.getFrom(), c1.getTo(), c2.getFrom(), c2.getTo()));
431 : }
432 13615 : if (isFoes && state[i1] == 's') {
433 16 : myRightOnRedConflicts.insert(std::make_pair(i1, i2));
434 : //std::cout << getID() << " prog=" << getProgramID() << " phase=" << (it - phases.begin()) << " rightOnRedConflict i1=" << i1 << " i2=" << i2 << "\n";
435 : }
436 : //std::cout << getID() << " i1=" << i1 << " i2=" << i2 << " rightTurnConflict=" << rightTurnConflict << " forbidden=" << forbidden << " isFoes=" << isFoes << "\n";
437 : }
438 : }
439 : }
440 : }
441 497 : }
442 : }
443 500 : myNeedsContRelationReady = true;
444 500 : myRightOnRedConflictsReady = true;
445 500 : }
446 :
447 :
448 : bool
449 40060 : NBLoadedSUMOTLDef::rightOnRedConflict(int index, int foeIndex) const {
450 40060 : if (amInvalid()) {
451 : return false;
452 : }
453 40060 : if (!myRightOnRedConflictsReady) {
454 340 : initNeedsContRelation();
455 : assert(myRightOnRedConflictsReady);
456 : }
457 40060 : return std::find(myRightOnRedConflicts.begin(), myRightOnRedConflicts.end(), std::make_pair(index, foeIndex)) != myRightOnRedConflicts.end();
458 : }
459 :
460 :
461 : void
462 12 : NBLoadedSUMOTLDef::registerModifications(bool addedConnections, bool removedConnections) {
463 12 : myReconstructAddedConnections |= addedConnections;
464 12 : myReconstructRemovedConnections |= removedConnections;
465 12 : }
466 :
467 : bool
468 219 : NBLoadedSUMOTLDef::isValid(const NBConnection& con) const {
469 : return (// edge still exists
470 219 : std::find(myIncomingEdges.begin(), myIncomingEdges.end(), con.getFrom()) != myIncomingEdges.end()
471 : // connection still exists
472 213 : && con.getFrom()->hasConnectionTo(con.getTo(), con.getToLane(), con.getFromLane())
473 : // connection is still set to be controlled
474 415 : && con.getFrom()->mayBeTLSControlled(con.getFromLane(), con.getTo(), con.getToLane()));
475 : }
476 :
477 : void
478 622 : NBLoadedSUMOTLDef::reconstructLogic() {
479 622 : const bool netedit = NBNetBuilder::runningNetedit();
480 : #ifdef DEBUG_RECONSTRUCTION
481 : bool debugPrintModified = myReconstructAddedConnections || myReconstructRemovedConnections;
482 : std::cout << getID() << " reconstructLogic added=" << myReconstructAddedConnections
483 : << " removed=" << myReconstructRemovedConnections
484 : << " valid=" << hasValidIndices()
485 : << " phasesLoaded=" << myPhasesLoaded
486 : << " oldLinks:\n";
487 : for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); ++it) {
488 : std::cout << " " << *it << "\n";
489 : }
490 : #endif
491 622 : if (myReconstructAddedConnections) {
492 5 : myReconstructAddedConnections = false;
493 : // do not rebuild the logic when running netedit and all links are already covered by the program
494 5 : if (!myPhasesLoaded && !(netedit && hasValidIndices())) {
495 : // rebuild the logic from scratch
496 : // XXX if a connection with the same from- and to-edge already exisits, its states could be copied instead
497 3 : NBOwnTLDef dummy(DummyID, myControlledNodes, 0, getType());
498 3 : dummy.setParticipantsInformation();
499 : dummy.setProgramID(getProgramID());
500 3 : dummy.setTLControllingInformation();
501 3 : NBTrafficLightLogic* newLogic = dummy.compute(OptionsCont::getOptions());
502 3 : myIncomingEdges = dummy.getIncomingEdges();
503 3 : myControlledLinks = dummy.getControlledLinks();
504 6 : for (std::vector<NBNode*>::const_iterator i = myControlledNodes.begin(); i != myControlledNodes.end(); i++) {
505 3 : (*i)->removeTrafficLight(&dummy);
506 : }
507 3 : delete myTLLogic;
508 3 : myTLLogic = newLogic;
509 3 : if (newLogic != nullptr) {
510 3 : newLogic->setID(getID());
511 : newLogic->setType(getType());
512 : newLogic->setOffset(getOffset());
513 3 : setTLControllingInformation();
514 : // reset crossing custom indices
515 6 : for (NBNode* n : myControlledNodes) {
516 3 : for (NBNode::Crossing* c : n->getCrossings()) {
517 0 : c->customTLIndex = NBConnection::InvalidTlIndex;
518 3 : }
519 : }
520 :
521 : }
522 3 : } else {
523 2 : setTLControllingInformation();
524 : }
525 : }
526 622 : if (myReconstructRemovedConnections) {
527 16 : myReconstructRemovedConnections = false;
528 : // for each connection, check whether it is still valid
529 216 : for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end();) {
530 200 : const NBConnection con = (*it);
531 200 : if (isValid(con)) {
532 : it++;
533 : } else {
534 : // remove connection
535 : const int removed = con.getTLIndex();
536 20 : it = myControlledLinks.erase(it);
537 : // no automatic modificaions when running netedit
538 20 : if (!myPhasesLoaded && !(netedit && hasValidIndices())) {
539 : // shift index off successive connections and remove entry from all phases if the tlIndex was only used by this connection
540 : bool exclusive = true;
541 169 : for (NBConnection& other : myControlledLinks) {
542 159 : if (other != con && other.getTLIndex() == removed) {
543 : exclusive = false;
544 : break;
545 : }
546 : }
547 20 : if (exclusive) {
548 : // shift indices above the removed index downward
549 132 : for (NBConnection& other : myControlledLinks) {
550 122 : if (other.getTLIndex() > removed) {
551 33 : other.setTLIndex(other.getTLIndex() - 1);
552 : }
553 : }
554 : // shift crossing custom indices above the removed index downward
555 20 : for (NBNode* n : myControlledNodes) {
556 10 : for (NBNode::Crossing* c : n->getCrossings()) {
557 0 : if (c->customTLIndex > removed) {
558 0 : c->customTLIndex--;
559 : }
560 10 : }
561 : }
562 : // rebuild the logic
563 10 : NBTrafficLightLogic* newLogic = new NBTrafficLightLogic(getID(), getProgramID(), 0, myOffset, myType);
564 76 : for (const NBTrafficLightLogic::PhaseDefinition& phase : myTLLogic->getPhases()) {
565 : std::string newState = phase.state;
566 : newState.erase(newState.begin() + removed);
567 66 : newLogic->addStep(phase.duration, newState);
568 : }
569 10 : delete myTLLogic;
570 10 : myTLLogic = newLogic;
571 : }
572 : }
573 : }
574 200 : }
575 16 : setTLControllingInformation();
576 : }
577 : #ifdef DEBUG_RECONSTRUCTION
578 : if (debugPrintModified) {
579 : std::cout << " newLinks:\n";
580 : for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); ++it) {
581 : std::cout << " " << *it << "\n";
582 : }
583 : }
584 : #endif
585 622 : }
586 :
587 :
588 : int
589 4 : NBLoadedSUMOTLDef::getMaxIndex() {
590 : int maxIndex = -1;
591 59 : for (const NBConnection& c : myControlledLinks) {
592 : maxIndex = MAX2(maxIndex, c.getTLIndex());
593 : maxIndex = MAX2(maxIndex, c.getTLIndex2());
594 : }
595 8 : for (NBNode* n : myControlledNodes) {
596 6 : for (NBNode::Crossing* c : n->getCrossings()) {
597 2 : maxIndex = MAX2(maxIndex, c->tlLinkIndex);
598 2 : maxIndex = MAX2(maxIndex, c->tlLinkIndex2);
599 4 : }
600 : }
601 4 : return maxIndex;
602 : }
603 :
604 :
605 : int
606 4 : NBLoadedSUMOTLDef::getMaxValidIndex() {
607 4 : return myTLLogic->getNumLinks() - 1;
608 : }
609 :
610 :
611 : bool
612 0 : NBLoadedSUMOTLDef::hasValidIndices() const {
613 0 : for (const NBConnection& c : myControlledLinks) {
614 0 : if (c.getTLIndex() == NBConnection::InvalidTlIndex) {
615 : return false;
616 : }
617 : }
618 0 : for (NBNode* n : myControlledNodes) {
619 0 : for (NBNode::Crossing* c : n->getCrossings()) {
620 0 : if (c->tlLinkIndex == NBConnection::InvalidTlIndex) {
621 : return false;
622 : }
623 0 : }
624 : }
625 : // method getMaxIndex() is const but cannot be declare as such due to inheritance
626 0 : return const_cast<NBLoadedSUMOTLDef*>(this)->getMaxIndex() < myTLLogic->getNumLinks();
627 : }
628 :
629 :
630 : std::string
631 268 : NBLoadedSUMOTLDef::getStates(int index) {
632 : assert(index >= 0);
633 : assert(index <= getMaxValidIndex());
634 : std::string result;
635 2082 : for (auto& pd : myTLLogic->getPhases()) {
636 1814 : result += pd.state[index];
637 : }
638 268 : return result;
639 : }
640 :
641 : bool
642 5152 : NBLoadedSUMOTLDef::isUsed(int index) const {
643 37961 : for (const NBConnection& c : myControlledLinks) {
644 37824 : if (c.getTLIndex() == index || c.getTLIndex2() == index) {
645 : return true;
646 : }
647 : }
648 183 : for (NBNode* n : myControlledNodes) {
649 274 : for (NBNode::Crossing* c : n->getCrossings()) {
650 228 : if (c->tlLinkIndex == index || c->tlLinkIndex2 == index) {
651 : return true;
652 : }
653 138 : }
654 : }
655 : return false;
656 : }
657 :
658 : std::set<const NBEdge*>
659 78 : NBLoadedSUMOTLDef::getEdgesUsingIndex(int index) const {
660 : std::set<const NBEdge*> result;
661 1304 : for (const NBConnection& c : myControlledLinks) {
662 1226 : if (c.getTLIndex() == index || c.getTLIndex2() == index) {
663 74 : result.insert(c.getFrom());
664 : }
665 : }
666 78 : return result;
667 : }
668 :
669 :
670 : void
671 59 : NBLoadedSUMOTLDef::replaceIndex(int oldIndex, int newIndex) {
672 59 : if (oldIndex == newIndex) {
673 : return;
674 : }
675 913 : for (NBConnection& c : myControlledLinks) {
676 858 : if (c.getTLIndex() == oldIndex) {
677 : c.setTLIndex(newIndex);
678 : }
679 858 : if (c.getTLIndex2() == oldIndex) {
680 : c.setTLIndex2(newIndex);
681 : }
682 : }
683 110 : for (NBNode* n : myControlledNodes) {
684 69 : for (NBNode::Crossing* c : n->getCrossings()) {
685 14 : if (c->tlLinkIndex == oldIndex) {
686 2 : c->tlLinkIndex = newIndex;
687 : }
688 14 : if (c->tlLinkIndex2 == oldIndex) {
689 2 : c->tlLinkIndex2 = newIndex;
690 : }
691 55 : }
692 : }
693 : }
694 :
695 : void
696 4 : NBLoadedSUMOTLDef::groupSignals() {
697 4 : const int maxIndex = getMaxIndex();
698 : std::vector<int> unusedIndices;
699 63 : for (int i = 0; i <= maxIndex; i++) {
700 59 : if (isUsed(i)) {
701 30 : std::set<const NBEdge*> edges = getEdgesUsingIndex(i);
702 : // compactify
703 30 : replaceIndex(i, i - (int)unusedIndices.size());
704 30 : if (edges.size() == 0) {
705 : // do not group pedestrian crossing signals
706 : continue;
707 : }
708 26 : std::string states = getStates(i);
709 240 : for (int j = i + 1; j <= maxIndex; j++) {
710 : // only group signals from the same edges as is commonly done by
711 : // traffic engineers
712 262 : if (states == getStates(j) && edges == getEdgesUsingIndex(j)) {
713 29 : replaceIndex(j, i - (int)unusedIndices.size());
714 : }
715 : }
716 : } else {
717 29 : unusedIndices.push_back(i);
718 : }
719 : }
720 33 : for (int i = (int)unusedIndices.size() - 1; i >= 0; i--) {
721 29 : myTLLogic->deleteStateIndex(unusedIndices[i]);
722 : }
723 4 : cleanupStates();
724 : //std::cout << "oldMaxIndex=" << maxIndex << " newMaxIndex=" << getMaxIndex() << " unused=" << toString(unusedIndices) << "\n";
725 4 : setTLControllingInformation();
726 : // patch crossing indices
727 8 : for (NBNode* n : myControlledNodes) {
728 6 : for (NBNode::Crossing* c : n->getCrossings()) {
729 6 : for (int i = (int)unusedIndices.size() - 1; i >= 0; i--) {
730 4 : if (c->customTLIndex > i) {
731 4 : c->customTLIndex--;
732 : }
733 4 : if (c->customTLIndex2 > i) {
734 4 : c->customTLIndex2--;
735 : }
736 : }
737 4 : }
738 : }
739 4 : }
740 :
741 : void
742 2 : NBLoadedSUMOTLDef::ungroupSignals() {
743 : NBConnectionVector defaultOrdering;
744 2 : collectAllLinks(defaultOrdering);
745 : std::vector<std::string> states; // organized per link rather than phase
746 : int index = 0;
747 26 : for (NBConnection& c : defaultOrdering) {
748 24 : NBConnection& c2 = *find_if(myControlledLinks.begin(), myControlledLinks.end(), connection_equal(c));
749 24 : states.push_back(getStates(c2.getTLIndex()));
750 24 : c2.setTLIndex(index++);
751 : }
752 4 : for (NBNode* n : myControlledNodes) {
753 4 : for (NBNode::Crossing* c : n->getCrossings()) {
754 2 : states.push_back(getStates(c->tlLinkIndex));
755 2 : c->customTLIndex = index++;
756 2 : if (c->tlLinkIndex2 != NBConnection::InvalidTlIndex) {
757 2 : states.push_back(getStates(c->tlLinkIndex2));
758 2 : c->customTLIndex2 = index++;
759 : }
760 2 : }
761 : }
762 2 : myTLLogic->setStateLength(index);
763 30 : for (int i = 0; i < (int)states.size(); i++) {
764 228 : for (int p = 0; p < (int)states[i].size(); p++) {
765 200 : myTLLogic->setPhaseState(p, i, (LinkState)states[i][p]);
766 : }
767 : }
768 2 : setTLControllingInformation();
769 2 : }
770 :
771 :
772 : void
773 0 : NBLoadedSUMOTLDef::copyIndices(NBTrafficLightDefinition* def) {
774 : std::map<int, std::string> oldStates; // organized per link index rather than phase
775 : std::map<int, std::string> newStates; // organized per link index rather than phase
776 0 : for (NBConnection& c : def->getControlledLinks()) {
777 0 : NBConnection& c2 = *find_if(myControlledLinks.begin(), myControlledLinks.end(), connection_equal(c));
778 0 : const int oldIndex = c2.getTLIndex();
779 0 : const int newIndex = c.getTLIndex();
780 0 : std::string states = getStates(oldIndex);
781 0 : oldStates[oldIndex] = states;
782 0 : if (newStates.count(newIndex) != 0 && newStates[newIndex] != states) {
783 0 : WRITE_WARNING("Signal groups from program '" + def->getProgramID() + "' are incompatible with the states of program '" + getProgramID() + "' at tlLogic '" + getID()
784 : + "'. Possibly unsafe program.");
785 : } else {
786 0 : newStates[newIndex] = states;
787 : }
788 0 : c2.setTLIndex(newIndex);
789 : }
790 0 : const int maxIndex = getMaxIndex();
791 0 : myTLLogic->setStateLength(maxIndex + 1);
792 0 : for (int i = 0; i < (int)newStates.size(); i++) {
793 0 : for (int p = 0; p < (int)newStates[i].size(); p++) {
794 0 : myTLLogic->setPhaseState(p, i, (LinkState)newStates[i][p]);
795 : }
796 : }
797 0 : setTLControllingInformation();
798 0 : }
799 :
800 :
801 : bool
802 4 : NBLoadedSUMOTLDef::cleanupStates() {
803 4 : const int maxIndex = getMaxValidIndex();
804 : std::vector<int> unusedIndices;
805 34 : for (int i = 0; i <= maxIndex; i++) {
806 30 : if (isUsed(i)) {
807 30 : if (unusedIndices.size() > 0) {
808 0 : replaceIndex(i, i - (int)unusedIndices.size());
809 : }
810 : } else {
811 0 : unusedIndices.push_back(i);
812 : }
813 : }
814 4 : for (int i = (int)unusedIndices.size() - 1; i >= 0; i--) {
815 0 : myTLLogic->deleteStateIndex(unusedIndices[i]);
816 : }
817 4 : if (unusedIndices.size() > 0) {
818 0 : myTLLogic->setStateLength(maxIndex + 1 - (int)unusedIndices.size());
819 0 : setTLControllingInformation();
820 : return true;
821 : } else {
822 : return false;
823 : }
824 4 : }
825 :
826 :
827 : void
828 0 : NBLoadedSUMOTLDef::joinLogic(NBTrafficLightDefinition* def) {
829 0 : def->setParticipantsInformation();
830 0 : def->compute(OptionsCont::getOptions());
831 0 : const int maxIndex = MAX2(getMaxIndex(), def->getMaxIndex());
832 0 : myTLLogic->setStateLength(maxIndex + 1);
833 0 : myControlledLinks.insert(myControlledLinks.end(), def->getControlledLinks().begin(), def->getControlledLinks().end());
834 0 : }
835 :
836 : bool
837 2 : NBLoadedSUMOTLDef::usingSignalGroups() const {
838 : // count how often each index is used
839 : std::map<int, int> indexUsage;
840 26 : for (const NBConnection& c : myControlledLinks) {
841 24 : indexUsage[c.getTLIndex()]++;
842 : }
843 4 : for (NBNode* n : myControlledNodes) {
844 4 : for (NBNode::Crossing* c : n->getCrossings()) {
845 2 : indexUsage[c->tlLinkIndex]++;
846 2 : indexUsage[c->tlLinkIndex2]++;
847 2 : }
848 : }
849 2 : for (auto it : indexUsage) {
850 2 : if (it.first >= 0 && it.second > 1) {
851 : return true;
852 : }
853 : }
854 : return false;
855 : }
856 :
857 : void
858 1 : NBLoadedSUMOTLDef::guessMinMaxDuration() {
859 : bool hasMinMaxDur = false;
860 9 : for (auto phase : myTLLogic->getPhases()) {
861 8 : if (phase.maxDur != UNSPECIFIED_DURATION) {
862 : //std::cout << " phase=" << phase.state << " maxDur=" << phase.maxDur << "\n";
863 : hasMinMaxDur = true;
864 : }
865 8 : }
866 1 : if (!hasMinMaxDur) {
867 1 : const SUMOTime minMinDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.min-dur"));
868 1 : const SUMOTime maxDur = TIME2STEPS(OptionsCont::getOptions().getInt("tls.max-dur"));
869 : std::set<int> yellowIndices;
870 9 : for (auto phase : myTLLogic->getPhases()) {
871 168 : for (int i = 0; i < (int)phase.state.size(); i++) {
872 160 : if (phase.state[i] == 'y' || phase.state[i] == 'Y') {
873 : yellowIndices.insert(i);
874 : }
875 : }
876 8 : }
877 9 : for (int ip = 0; ip < (int)myTLLogic->getPhases().size(); ip++) {
878 : bool needMinMaxDur = false;
879 8 : auto phase = myTLLogic->getPhases()[ip];
880 : std::set<int> greenIndices;
881 8 : if (phase.state.find_first_of("yY") != std::string::npos) {
882 : continue;
883 : }
884 84 : for (int i = 0; i < (int)phase.state.size(); i++) {
885 80 : if (yellowIndices.count(i) != 0 && phase.state[i] == 'G') {
886 : needMinMaxDur = true;
887 : greenIndices.insert(i);
888 : }
889 : }
890 4 : if (needMinMaxDur) {
891 : double maxSpeed = 0;
892 84 : for (NBConnection& c : myControlledLinks) {
893 : if (greenIndices.count(c.getTLIndex()) != 0) {
894 20 : maxSpeed = MAX2(maxSpeed, c.getFrom()->getLaneSpeed(c.getFromLane()));
895 : }
896 : }
897 : // 5s at 50km/h, 10s at 80km/h, rounded to full seconds
898 4 : const double minDurBySpeed = maxSpeed * 3.6 / 6 - 3.3;
899 4 : SUMOTime minDur = MAX2(minMinDur, TIME2STEPS(floor(minDurBySpeed + 0.5)));
900 4 : myTLLogic->setPhaseMinDuration(ip, minDur);
901 4 : myTLLogic->setPhaseMaxDuration(ip, maxDur);
902 : }
903 8 : }
904 : }
905 1 : }
906 :
907 :
908 : void
909 511 : NBLoadedSUMOTLDef::finalChecks() const {
910 5558 : for (int i = 0; i < myTLLogic->getNumLinks(); i++) {
911 5063 : if (!isUsed(i)) {
912 48 : WRITE_WARNINGF(TL("Unused state in tlLogic '%', program '%' at tl-index %"), getID(), getProgramID(), i);
913 16 : break;
914 : }
915 : }
916 511 : }
917 :
918 :
919 : /****************************************************************************/
|