Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-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 NBTrafficLightLogicCont.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @date Sept 2002
19 : ///
20 : // A container for traffic light definitions and built programs
21 : /****************************************************************************/
22 : #include <config.h>
23 : #include <map>
24 : #include <string>
25 : #include <algorithm>
26 : #include <utils/common/MsgHandler.h>
27 : #include <utils/common/ToString.h>
28 : #include <utils/common/IDSupplier.h>
29 : #include <utils/iodevices/OutputDevice.h>
30 : #include <utils/options/OptionsCont.h>
31 : #include "NBTrafficLightLogic.h"
32 : #include "NBTrafficLightLogicCont.h"
33 : #include "NBOwnTLDef.h"
34 : #include "NBLoadedSUMOTLDef.h"
35 : #include "NBEdgeCont.h"
36 : #include "NBNodeCont.h"
37 :
38 :
39 : // ===========================================================================
40 : // static members
41 : // ===========================================================================
42 : const NBTrafficLightLogicCont::Program2Def NBTrafficLightLogicCont::EmptyDefinitions = NBTrafficLightLogicCont::Program2Def();
43 :
44 : // ===========================================================================
45 : // method definitions
46 : // ===========================================================================
47 1993 : NBTrafficLightLogicCont::NBTrafficLightLogicCont() {}
48 :
49 :
50 1993 : NBTrafficLightLogicCont::~NBTrafficLightLogicCont() {
51 1993 : clear();
52 1993 : }
53 :
54 :
55 : void
56 1983 : NBTrafficLightLogicCont::applyOptions(OptionsCont& oc) {
57 : // check whether any offsets shall be manipulated by setting
58 : // them to half of the duration
59 3966 : if (oc.isSet("tls.half-offset")) {
60 2 : std::vector<std::string> ids = oc.getStringVector("tls.half-offset");
61 : myHalfOffsetTLS.insert(ids.begin(), ids.end());
62 1 : }
63 : // check whether any offsets shall be manipulated by setting
64 : // them to a quarter of the duration
65 3966 : if (oc.isSet("tls.quarter-offset")) {
66 2 : std::vector<std::string> ids = oc.getStringVector("tls.quarter-offset");
67 : myQuarterOffsetTLS.insert(ids.begin(), ids.end());
68 1 : }
69 1983 : }
70 :
71 :
72 : std::string
73 0 : NBTrafficLightLogicCont::getNextProgramID(const std::string& id) const {
74 0 : IDSupplier idS("", 0);
75 : if (myDefinitions.count(id)) {
76 : const Program2Def& programs = myDefinitions.find(id)->second;
77 0 : for (auto item : programs) {
78 0 : idS.avoid(item.first);
79 : }
80 : }
81 0 : return idS.getNext();
82 0 : }
83 :
84 :
85 : bool
86 4056 : NBTrafficLightLogicCont::insert(NBTrafficLightDefinition* logic, bool forceInsert) {
87 : myExtracted.erase(logic);
88 4056 : if (myDefinitions.count(logic->getID())) {
89 50 : if (myDefinitions[logic->getID()].count(logic->getProgramID())) {
90 2 : if (forceInsert) {
91 0 : logic->setProgramID(getNextProgramID(logic->getID()));
92 : } else {
93 : return false;
94 : }
95 : }
96 : } else {
97 8012 : myDefinitions[logic->getID()] = Program2Def();
98 : }
99 4054 : myDefinitions[logic->getID()][logic->getProgramID()] = logic;
100 4054 : return true;
101 : }
102 :
103 :
104 : bool
105 154 : NBTrafficLightLogicCont::removeFully(const std::string id) {
106 : if (myDefinitions.count(id)) {
107 : // delete all programs
108 298 : for (Program2Def::iterator i = myDefinitions[id].begin(); i != myDefinitions[id].end(); i++) {
109 149 : delete i->second;
110 : }
111 : myDefinitions.erase(id);
112 : // also delete any logics that were already computed
113 : if (myComputed.count(id)) {
114 10 : for (Program2Logic::iterator i = myComputed[id].begin(); i != myComputed[id].end(); i++) {
115 5 : delete i->second;
116 : }
117 : myComputed.erase(id);
118 : }
119 149 : return true;
120 : } else {
121 5 : return false;
122 : }
123 : }
124 :
125 :
126 : bool
127 970 : NBTrafficLightLogicCont::removeProgram(const std::string id, const std::string programID, bool del) {
128 970 : if (myDefinitions.count(id) && myDefinitions[id].count(programID)) {
129 965 : if (del) {
130 35 : delete myDefinitions[id][programID];
131 : }
132 965 : myDefinitions[id].erase(programID);
133 965 : return true;
134 : } else {
135 5 : return false;
136 : }
137 : }
138 :
139 :
140 : void
141 935 : NBTrafficLightLogicCont::extract(NBTrafficLightDefinition* definition) {
142 : myExtracted.insert(definition);
143 2805 : removeProgram(definition->getID(), definition->getProgramID(), false);
144 935 : }
145 :
146 :
147 : bool
148 2 : NBTrafficLightLogicCont::exist(const std::string& newID, bool requireComputed) const {
149 : const auto itD = myDefinitions.find(newID);
150 : const auto itC = myComputed.find(newID);
151 2 : if ((itD != myDefinitions.end()) && (itC != myComputed.end() || !requireComputed)) {
152 1 : return true;
153 : } else {
154 : return false;
155 : }
156 : }
157 :
158 :
159 : std::pair<int, int>
160 1694 : NBTrafficLightLogicCont::computeLogics(OptionsCont& oc) {
161 : // clean previous logics
162 1694 : Logics logics = getComputed();
163 1694 : for (Logics::iterator it = logics.begin(); it != logics.end(); it++) {
164 0 : delete *it;
165 : }
166 : myComputed.clear();
167 3388 : if (oc.getBool("tls.rebuild")) {
168 17 : for (NBTrafficLightDefinition* def : getDefinitions()) {
169 11 : NBLoadedSUMOTLDef* lDef = dynamic_cast<NBLoadedSUMOTLDef*>(def);
170 11 : if (lDef != nullptr) {
171 : TrafficLightType type = def->getType();
172 22 : if (!oc.isDefault("tls.default-type")) {
173 14 : type = SUMOXMLDefinitions::TrafficLightTypes.get(oc.getString("tls.default-type"));
174 : }
175 11 : NBOwnTLDef* oDef = new NBOwnTLDef(def->getID(), def->getNodes(), def->getOffset(), type);
176 11 : oDef->setProgramID(def->getProgramID());
177 11 : oDef->setParticipantsInformation();
178 49 : for (NBEdge* e : oDef->getIncomingEdges()) {
179 38 : e->clearControllingTLInformation();
180 : }
181 11 : oDef->setTLControllingInformation();
182 22 : for (NBNode* node : oDef->getNodes()) {
183 11 : node->removeTrafficLight(def);
184 : }
185 22 : removeProgram(def->getID(), def->getProgramID());
186 11 : insert(oDef);
187 : }
188 6 : }
189 : }
190 3388 : if (oc.getBool("tls.group-signals")) {
191 : // replace NBOwnTLDef tld with NBLoadedSUMOTLDef
192 8 : for (NBTrafficLightDefinition* def : getDefinitions()) {
193 4 : NBLoadedSUMOTLDef* lDef = dynamic_cast<NBLoadedSUMOTLDef*>(def);
194 4 : if (lDef == nullptr) {
195 2 : NBTrafficLightLogic* logic = def->compute(oc);
196 2 : if (logic != nullptr) {
197 2 : lDef = new NBLoadedSUMOTLDef(*def, *logic);
198 2 : lDef->setParticipantsInformation();
199 4 : for (NBNode* node : lDef->getNodes()) {
200 2 : node->removeTrafficLight(def);
201 2 : node->addTrafficLight(lDef);
202 : }
203 4 : removeProgram(def->getID(), def->getProgramID());
204 2 : insert(lDef);
205 2 : delete logic;
206 : }
207 : }
208 2 : if (lDef != nullptr) {
209 4 : lDef->groupSignals();
210 : }
211 4 : }
212 3380 : } else if (oc.getBool("tls.ungroup-signals")) {
213 4 : for (NBTrafficLightDefinition* def : getDefinitions()) {
214 2 : NBLoadedSUMOTLDef* lDef = dynamic_cast<NBLoadedSUMOTLDef*>(def);
215 : // NBOwnTLDef are always ungrouped
216 2 : if (lDef != nullptr) {
217 2 : if (lDef->usingSignalGroups()) {
218 2 : lDef->ungroupSignals();
219 : }
220 : }
221 2 : }
222 : }
223 : int numPrograms = 0;
224 4631 : for (NBTrafficLightDefinition* def : getDefinitions()) {
225 2937 : if (computeSingleLogic(oc, def)) {
226 2065 : numPrograms++;
227 : }
228 1694 : }
229 1694 : return std::pair<int, int>((int)myComputed.size(), numPrograms);
230 1694 : }
231 :
232 :
233 : bool
234 3666 : NBTrafficLightLogicCont::computeSingleLogic(OptionsCont& oc, NBTrafficLightDefinition* def) {
235 3666 : if (def->getNodes().size() == 0) {
236 : return false;
237 : }
238 : const std::string& id = def->getID();
239 : const std::string& programID = def->getProgramID();
240 : // build program
241 2858 : NBTrafficLightLogic* built = def->compute(oc);
242 2858 : if (built == nullptr) {
243 192 : WRITE_WARNINGF(TL("Could not build program '%' for traffic light '%'"), programID, id);
244 64 : return false;
245 : }
246 : // compute offset
247 2794 : SUMOTime T = built->getDuration();
248 : if (myHalfOffsetTLS.count(id)) {
249 2 : built->setOffset(TIME2STEPS(floor(STEPS2TIME(T / 2))));
250 : }
251 : if (myQuarterOffsetTLS.count(id)) {
252 2 : built->setOffset(TIME2STEPS(floor(STEPS2TIME(T / 4))));
253 : }
254 : // and insert the result after computation
255 : // make sure we don't leak memory if computeSingleLogic is called externally
256 2794 : if (myComputed[id][programID] != nullptr) {
257 729 : delete myComputed[id][programID];
258 : }
259 2794 : myComputed[id][programID] = built;
260 2794 : return true;
261 : }
262 :
263 :
264 : void
265 1993 : NBTrafficLightLogicCont::clear() {
266 1993 : Definitions definitions = getDefinitions();
267 4933 : for (Definitions::iterator it = definitions.begin(); it != definitions.end(); it++) {
268 2940 : delete *it;
269 : }
270 : myDefinitions.clear();
271 1993 : Logics logics = getComputed();
272 4053 : for (Logics::iterator it = logics.begin(); it != logics.end(); it++) {
273 2060 : delete *it;
274 : }
275 : myComputed.clear();
276 2923 : for (std::set<NBTrafficLightDefinition*>::iterator it = myExtracted.begin(); it != myExtracted.end(); it++) {
277 930 : delete *it;
278 : }
279 : myExtracted.clear();
280 1993 : }
281 :
282 :
283 : void
284 0 : NBTrafficLightLogicCont::remapRemoved(NBEdge* removed, const EdgeVector& incoming,
285 : const EdgeVector& outgoing) {
286 0 : Definitions definitions = getDefinitions();
287 0 : for (Definitions::iterator it = definitions.begin(); it != definitions.end(); it++) {
288 0 : (*it)->remapRemoved(removed, incoming, outgoing);
289 : }
290 0 : }
291 :
292 :
293 : void
294 54 : NBTrafficLightLogicCont::replaceRemoved(NBEdge* removed, int removedLane,
295 : NBEdge* by, int byLane, bool incoming) {
296 54 : Definitions definitions = getDefinitions();
297 340 : for (Definitions::iterator it = definitions.begin(); it != definitions.end(); it++) {
298 286 : (*it)->replaceRemoved(removed, removedLane, by, byLane, incoming);
299 : }
300 54 : }
301 :
302 :
303 : NBTrafficLightDefinition*
304 2190 : NBTrafficLightLogicCont::getDefinition(const std::string& id, const std::string& programID) const {
305 : Id2Defs::const_iterator i = myDefinitions.find(id);
306 2190 : if (i != myDefinitions.end()) {
307 : Program2Def programs = i->second;
308 : Program2Def::const_iterator i2 = programs.find(programID);
309 2190 : if (i2 != programs.end()) {
310 2175 : return i2->second;
311 : }
312 : }
313 : return nullptr;
314 : }
315 :
316 : const NBTrafficLightLogicCont::Program2Def&
317 5810 : NBTrafficLightLogicCont::getPrograms(const std::string& id) const {
318 : Id2Defs::const_iterator it = myDefinitions.find(id);
319 5810 : if (it != myDefinitions.end()) {
320 5489 : return it->second;
321 : } else {
322 : return EmptyDefinitions;
323 : }
324 : }
325 :
326 :
327 : NBTrafficLightLogic*
328 0 : NBTrafficLightLogicCont::getLogic(const std::string& id, const std::string& programID) const {
329 : Id2Logics::const_iterator i = myComputed.find(id);
330 0 : if (i != myComputed.end()) {
331 : Program2Logic programs = i->second;
332 : Program2Logic::const_iterator i2 = programs.find(programID);
333 0 : if (i2 != programs.end()) {
334 0 : return i2->second;
335 : }
336 : }
337 : return nullptr;
338 : }
339 :
340 :
341 : void
342 1695 : NBTrafficLightLogicCont::setTLControllingInformation(const NBEdgeCont& ec, const NBNodeCont& nc) {
343 1695 : Definitions definitions = getDefinitions();
344 : // set the information about all participants, first
345 4634 : for (Definitions::iterator it = definitions.begin(); it != definitions.end(); it++) {
346 2939 : (*it)->setParticipantsInformation();
347 : }
348 : // clear previous information because tlDefs may have been removed in netedit
349 1695 : ec.clearControllingTLInformation();
350 : // insert the information about the tl-controlling
351 4632 : for (Definitions::iterator it = definitions.begin(); it != definitions.end(); it++) {
352 2938 : (*it)->setTLControllingInformation();
353 : }
354 : // handle rail signals which are not instantiated as normal definitions
355 52452 : for (std::map<std::string, NBNode*>::const_iterator it = nc.begin(); it != nc.end(); it ++) {
356 50758 : NBNode* n = it->second;
357 50758 : if (n->getType() == SumoXMLNodeType::RAIL_SIGNAL || n->getType() == SumoXMLNodeType::RAIL_CROSSING) {
358 1543 : NBOwnTLDef dummy(n->getID(), n, 0, TrafficLightType::STATIC);
359 1543 : dummy.setParticipantsInformation();
360 1543 : dummy.setTLControllingInformation();
361 1543 : n->setCrossingTLIndices(dummy.getID(), (int)dummy.getControlledLinks().size());
362 1543 : n->removeTrafficLight(&dummy);
363 1543 : }
364 : }
365 1695 : }
366 :
367 :
368 : void
369 22 : NBTrafficLightLogicCont::setOpenDriveSignalParameters() {
370 22 : Definitions definitions = getDefinitions();
371 25 : for (NBTrafficLightDefinition* def : getDefinitions()) {
372 : std::map<NBEdge*, std::string> defaultSignalIDs;
373 28 : for (const NBConnection& con : def->getControlledLinks()) {
374 25 : const NBEdge::Connection& c = con.getFrom()->getConnection(con.getFromLane(), con.getTo(), con.getToLane());
375 50 : if (c.hasParameter("signalID")) {
376 75 : defaultSignalIDs[con.getFrom()] = c.getParameter("signalID");
377 100 : def->setParameter("linkSignalID:" + toString(con.getTLIndex()), c.getParameter("signalID"));
378 : }
379 : }
380 : // oftentimes, signals are placed on connecting road but are meant to apply to all connections from the incoming edge
381 28 : for (const NBConnection& con : def->getControlledLinks()) {
382 25 : const NBEdge::Connection& c = con.getFrom()->getConnection(con.getFromLane(), con.getTo(), con.getToLane());
383 50 : if (!c.hasParameter("signalID") && defaultSignalIDs.count(con.getFrom()) != 0) {
384 0 : WRITE_WARNINGF(TL("Guessing signalID for link index % at traffic light '%'."), con.getTLIndex(), def->getID());
385 0 : def->setParameter("linkSignalID:" + toString(con.getTLIndex()), defaultSignalIDs[con.getFrom()]);
386 : }
387 : }
388 22 : }
389 22 : }
390 :
391 :
392 : void
393 0 : NBTrafficLightLogicCont::applyOpenDriveControllers(OptionsCont& oc) {
394 : // collect connections with controllerID which can be controlled together
395 0 : Definitions definitions = getDefinitions();
396 : std::map<std::string, NBTrafficLightDefinition*> defsToGroup;
397 : std::map<std::string, std::map<std::string, std::set<int>>> controllerID2tlIndex;
398 0 : for (NBTrafficLightDefinition* def : definitions) {
399 : bool makeGroups = false;
400 : NBConnectionVector& connections = def->getControlledLinks();
401 : std::string defID = def->getID();
402 :
403 0 : for (NBConnection conn : connections) {
404 0 : std::string controllerID = conn.getFrom()->getConnection(conn.getFromLane(), conn.getTo(), conn.getToLane()).getParameter("controllerID");
405 0 : if (controllerID != "") {
406 0 : if (controllerID2tlIndex.find(defID) == controllerID2tlIndex.end()) {
407 0 : controllerID2tlIndex[defID][controllerID] = std::set<int>({ conn.getTLIndex() });
408 0 : } else if (controllerID2tlIndex[defID].find(controllerID) != controllerID2tlIndex[defID].end()) {
409 0 : controllerID2tlIndex[defID][controllerID].insert(conn.getTLIndex());
410 : } else {
411 0 : controllerID2tlIndex[defID][controllerID] = std::set<int>({ conn.getTLIndex() });
412 : }
413 0 : makeGroups = controllerID2tlIndex[defID][controllerID].size() > 1 || makeGroups;
414 : }
415 0 : }
416 0 : if (makeGroups) {
417 0 : defsToGroup[def->getID()] = def;
418 : }
419 : }
420 : bool same = true;
421 0 : for (NBTrafficLightLogic* computed : getComputed()) {
422 : const std::string computedID = computed->getID();
423 0 : if (defsToGroup.find(computedID) != defsToGroup.end()) {
424 : // remember corresponding tl indices and check if they can be joined
425 : // execute tl index joins if possible
426 0 : const std::vector<NBTrafficLightLogic::PhaseDefinition> phases = computed->getPhases();
427 : std::vector<int> major2minor;
428 0 : for (const auto& indexSet : controllerID2tlIndex[computedID]) {
429 0 : if (indexSet.second.size() < 2) {
430 0 : continue;
431 : }
432 : bool toMinor = false;
433 0 : for (NBTrafficLightLogic::PhaseDefinition phase : phases) {
434 : std::set<int>::iterator it = indexSet.second.begin();
435 0 : char firstChar = phase.state[*it];
436 0 : if (firstChar == LINKSTATE_TL_GREEN_MAJOR) {
437 : firstChar = LINKSTATE_TL_GREEN_MINOR;
438 : }
439 : it++;
440 0 : for (; it != indexSet.second.end(); it++) {
441 0 : const char compareChar = (phase.state[*it] != LINKSTATE_TL_GREEN_MAJOR) ? phase.state[*it] : (char)LINKSTATE_TL_GREEN_MINOR;
442 0 : if (compareChar != firstChar) {
443 : same = false;
444 : break;
445 0 : } else if (phase.state[*it] == LINKSTATE_TL_GREEN_MINOR) {
446 : toMinor = true;
447 : }
448 : }
449 0 : if (!same) {
450 : break;
451 : }
452 0 : }
453 0 : if (toMinor) {
454 0 : major2minor.push_back(*indexSet.second.begin());
455 : }
456 : }
457 0 : if (same) {
458 0 : NBLoadedSUMOTLDef* lDef = dynamic_cast<NBLoadedSUMOTLDef*>(defsToGroup[computedID]);
459 0 : if (lDef == nullptr) {
460 0 : lDef = new NBLoadedSUMOTLDef(*defsToGroup[computedID], *computed);
461 0 : lDef->setParticipantsInformation();
462 0 : for (int index : major2minor) { // update the signal states (G -> g where needed)
463 0 : for (int i = 0; i < (int)phases.size(); i++) {
464 0 : if (phases[i].state[index] == LINKSTATE_TL_GREEN_MAJOR) {
465 0 : lDef->getLogic()->setPhaseState(i, index, LINKSTATE_TL_GREEN_MINOR);
466 : }
467 : }
468 : }
469 : // join signal groups, update the connections
470 : std::vector<int> indexToRemove;
471 0 : for (const auto& indexSet : controllerID2tlIndex[computedID]) {
472 0 : int minIndex = *indexSet.second.begin();
473 0 : for (int index : indexSet.second) {
474 0 : if (index != minIndex) {
475 0 : lDef->replaceIndex(index, minIndex);
476 0 : indexToRemove.push_back(index);
477 : }
478 : }
479 : }
480 : // remove unused indices from signal programs
481 0 : lDef->cleanupStates();
482 0 : for (NBNode* node : lDef->getNodes()) {
483 0 : node->removeTrafficLight(defsToGroup[computedID]);
484 0 : node->addTrafficLight(lDef);
485 : }
486 0 : removeProgram(defsToGroup[computedID]->getID(), defsToGroup[computedID]->getProgramID());
487 0 : insert(lDef);
488 0 : computeSingleLogic(oc, lDef);
489 0 : }
490 : } else { // TODO: try other strategy to build signal groups
491 0 : WRITE_WARNINGF(TL("Was not able to apply the OpenDRIVE signal group information onto the signal program of traffic light % generated by SUMO."), computed->getID());
492 : }
493 : break;
494 0 : }
495 0 : }
496 0 : }
497 :
498 :
499 : NBTrafficLightLogicCont::Logics
500 5464 : NBTrafficLightLogicCont::getComputed() const {
501 : Logics result;
502 9592 : for (Id2Logics::const_iterator it_id = myComputed.begin(); it_id != myComputed.end(); it_id++) {
503 : const Program2Logic& programs = it_id->second;
504 8279 : for (Program2Logic::const_iterator it_prog = programs.begin(); it_prog != programs.end(); it_prog++) {
505 4151 : result.push_back(it_prog->second);
506 : }
507 : }
508 5464 : return result;
509 0 : }
510 :
511 :
512 : NBTrafficLightLogicCont::Definitions
513 5492 : NBTrafficLightLogicCont::getDefinitions() const {
514 : Definitions result;
515 17400 : for (Id2Defs::const_iterator it_id = myDefinitions.begin(); it_id != myDefinitions.end(); it_id++) {
516 : const Program2Def& programs = it_id->second;
517 21033 : for (Program2Def::const_iterator it_prog = programs.begin(); it_prog != programs.end(); it_prog++) {
518 9125 : result.push_back(it_prog->second);
519 : }
520 : }
521 5492 : return result;
522 0 : }
523 :
524 :
525 : void
526 59 : NBTrafficLightLogicCont::rename(NBTrafficLightDefinition* tlDef, const std::string& newID) {
527 : auto it = myDefinitions.find(tlDef->getID());
528 59 : if (it != myDefinitions.end()) {
529 118 : for (auto item : it->second) {
530 59 : item.second->setID(newID);
531 : }
532 59 : myDefinitions[newID] = it->second;
533 : myDefinitions.erase(it);
534 : }
535 : auto it2 = myComputed.find(tlDef->getID());
536 59 : if (it2 != myComputed.end()) {
537 0 : for (auto item : it2->second) {
538 0 : item.second->setID(newID);
539 : }
540 0 : myComputed[newID] = it2->second;
541 : myComputed.erase(it2);
542 : }
543 59 : }
544 :
545 :
546 : int
547 54 : NBTrafficLightLogicCont::getNumExtracted() const {
548 54 : return (int)myExtracted.size();
549 : }
550 :
551 : /****************************************************************************/
|