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