Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-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 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 2113 : NBTrafficLightLogicCont::NBTrafficLightLogicCont() {}
48 :
49 :
50 2113 : NBTrafficLightLogicCont::~NBTrafficLightLogicCont() {
51 2113 : clear();
52 2113 : }
53 :
54 :
55 : void
56 2103 : NBTrafficLightLogicCont::applyOptions(OptionsCont& oc) {
57 : // check whether any offsets shall be manipulated by setting
58 : // them to half of the duration
59 4206 : 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 4206 : 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 2103 : }
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 4383 : NBTrafficLightLogicCont::insert(NBTrafficLightDefinition* logic, bool forceInsert) {
87 : myExtracted.erase(logic);
88 4383 : if (myDefinitions.count(logic->getID())) {
89 52 : 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 8662 : myDefinitions[logic->getID()] = Program2Def();
98 : }
99 4381 : myDefinitions[logic->getID()][logic->getProgramID()] = logic;
100 4381 : return true;
101 : }
102 :
103 :
104 : bool
105 156 : NBTrafficLightLogicCont::removeFully(const std::string id) {
106 : if (myDefinitions.count(id)) {
107 : // delete all programs
108 302 : for (Program2Def::iterator i = myDefinitions[id].begin(); i != myDefinitions[id].end(); i++) {
109 151 : 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 151 : return true;
120 : } else {
121 5 : return false;
122 : }
123 : }
124 :
125 :
126 : bool
127 990 : NBTrafficLightLogicCont::removeProgram(const std::string id, const std::string programID, bool del) {
128 990 : if (myDefinitions.count(id) && myDefinitions[id].count(programID)) {
129 985 : if (del) {
130 37 : delete myDefinitions[id][programID];
131 : }
132 985 : myDefinitions[id].erase(programID);
133 985 : return true;
134 : } else {
135 5 : return false;
136 : }
137 : }
138 :
139 :
140 : void
141 953 : NBTrafficLightLogicCont::extract(NBTrafficLightDefinition* definition) {
142 : myExtracted.insert(definition);
143 2859 : removeProgram(definition->getID(), definition->getProgramID(), false);
144 953 : }
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 1812 : NBTrafficLightLogicCont::computeLogics(OptionsCont& oc) {
161 : // clean previous logics
162 1812 : Logics logics = getComputed();
163 1812 : for (Logics::iterator it = logics.begin(); it != logics.end(); it++) {
164 0 : delete *it;
165 : }
166 : myComputed.clear();
167 3624 : if (oc.getBool("tls.rebuild")) {
168 19 : for (NBTrafficLightDefinition* def : getDefinitions()) {
169 12 : NBLoadedSUMOTLDef* lDef = dynamic_cast<NBLoadedSUMOTLDef*>(def);
170 12 : if (lDef != nullptr) {
171 : TrafficLightType type = def->getType();
172 24 : if (!oc.isDefault("tls.default-type")) {
173 14 : type = SUMOXMLDefinitions::TrafficLightTypes.get(oc.getString("tls.default-type"));
174 : }
175 12 : NBOwnTLDef* oDef = new NBOwnTLDef(def->getID(), def->getNodes(), def->getOffset(), type);
176 12 : oDef->setProgramID(def->getProgramID());
177 12 : oDef->setParticipantsInformation();
178 52 : for (NBEdge* e : oDef->getIncomingEdges()) {
179 40 : e->clearControllingTLInformation();
180 : }
181 12 : oDef->setTLControllingInformation();
182 24 : for (NBNode* node : oDef->getNodes()) {
183 12 : node->removeTrafficLight(def);
184 15 : for (auto c : node->getCrossings()) {
185 3 : c->customTLIndex = -1;
186 3 : c->customTLIndex2 = -1;
187 3 : c->tlLinkIndex2 = -1;
188 12 : }
189 : }
190 24 : removeProgram(def->getID(), def->getProgramID());
191 12 : insert(oDef);
192 : }
193 7 : }
194 : }
195 3624 : if (oc.getBool("tls.group-signals")) {
196 : // replace NBOwnTLDef tld with NBLoadedSUMOTLDef
197 8 : for (NBTrafficLightDefinition* def : getDefinitions()) {
198 4 : NBLoadedSUMOTLDef* lDef = dynamic_cast<NBLoadedSUMOTLDef*>(def);
199 4 : if (lDef == nullptr) {
200 2 : NBTrafficLightLogic* logic = def->compute(oc);
201 2 : if (logic != nullptr) {
202 2 : lDef = new NBLoadedSUMOTLDef(*def, *logic);
203 2 : lDef->setParticipantsInformation();
204 4 : for (NBNode* node : lDef->getNodes()) {
205 2 : node->removeTrafficLight(def);
206 2 : node->addTrafficLight(lDef);
207 : }
208 4 : removeProgram(def->getID(), def->getProgramID());
209 2 : insert(lDef);
210 2 : delete logic;
211 : }
212 : }
213 2 : if (lDef != nullptr) {
214 4 : lDef->groupSignals();
215 : }
216 4 : }
217 3616 : } else if (oc.getBool("tls.ungroup-signals")) {
218 4 : for (NBTrafficLightDefinition* def : getDefinitions()) {
219 2 : NBLoadedSUMOTLDef* lDef = dynamic_cast<NBLoadedSUMOTLDef*>(def);
220 : // NBOwnTLDef are always ungrouped
221 2 : if (lDef != nullptr) {
222 2 : if (lDef->usingSignalGroups()) {
223 2 : lDef->ungroupSignals();
224 : }
225 : }
226 2 : }
227 : }
228 : int numPrograms = 0;
229 5051 : for (NBTrafficLightDefinition* def : getDefinitions()) {
230 3239 : if (computeSingleLogic(oc, def)) {
231 2325 : numPrograms++;
232 : }
233 1812 : }
234 1812 : return std::pair<int, int>((int)myComputed.size(), numPrograms);
235 1812 : }
236 :
237 :
238 : bool
239 3975 : NBTrafficLightLogicCont::computeSingleLogic(OptionsCont& oc, NBTrafficLightDefinition* def) {
240 3975 : if (def->getNodes().size() == 0) {
241 : return false;
242 : }
243 : const std::string& id = def->getID();
244 : const std::string& programID = def->getProgramID();
245 : // build program
246 3126 : NBTrafficLightLogic* built = def->compute(oc);
247 3126 : if (built == nullptr) {
248 195 : WRITE_WARNINGF(TL("Could not build program '%' for traffic light '%'"), programID, id);
249 65 : return false;
250 : }
251 : // compute offset
252 3061 : SUMOTime T = built->getDuration();
253 : if (myHalfOffsetTLS.count(id)) {
254 2 : built->setOffset(TIME2STEPS(floor(STEPS2TIME(T / 2))));
255 : }
256 : if (myQuarterOffsetTLS.count(id)) {
257 2 : built->setOffset(TIME2STEPS(floor(STEPS2TIME(T / 4))));
258 : }
259 : // and insert the result after computation
260 : // make sure we don't leak memory if computeSingleLogic is called externally
261 3061 : if (myComputed[id][programID] != nullptr) {
262 736 : delete myComputed[id][programID];
263 : }
264 3061 : myComputed[id][programID] = built;
265 3061 : return true;
266 : }
267 :
268 :
269 : void
270 2113 : NBTrafficLightLogicCont::clear() {
271 2113 : Definitions definitions = getDefinitions();
272 5358 : for (Definitions::iterator it = definitions.begin(); it != definitions.end(); it++) {
273 3245 : delete *it;
274 : }
275 : myDefinitions.clear();
276 2113 : Logics logics = getComputed();
277 4433 : for (Logics::iterator it = logics.begin(); it != logics.end(); it++) {
278 2320 : delete *it;
279 : }
280 : myComputed.clear();
281 3061 : for (std::set<NBTrafficLightDefinition*>::iterator it = myExtracted.begin(); it != myExtracted.end(); it++) {
282 948 : delete *it;
283 : }
284 : myExtracted.clear();
285 2113 : }
286 :
287 :
288 : void
289 0 : NBTrafficLightLogicCont::remapRemoved(NBEdge* removed, const EdgeVector& incoming,
290 : const EdgeVector& outgoing) {
291 0 : Definitions definitions = getDefinitions();
292 0 : for (Definitions::iterator it = definitions.begin(); it != definitions.end(); it++) {
293 0 : (*it)->remapRemoved(removed, incoming, outgoing);
294 : }
295 0 : }
296 :
297 :
298 : void
299 54 : NBTrafficLightLogicCont::replaceRemoved(NBEdge* removed, int removedLane,
300 : NBEdge* by, int byLane, bool incoming) {
301 54 : Definitions definitions = getDefinitions();
302 340 : for (Definitions::iterator it = definitions.begin(); it != definitions.end(); it++) {
303 286 : (*it)->replaceRemoved(removed, removedLane, by, byLane, incoming);
304 : }
305 54 : }
306 :
307 :
308 : NBTrafficLightDefinition*
309 2453 : NBTrafficLightLogicCont::getDefinition(const std::string& id, const std::string& programID) const {
310 : Id2Defs::const_iterator i = myDefinitions.find(id);
311 2453 : if (i != myDefinitions.end()) {
312 : Program2Def programs = i->second;
313 : Program2Def::const_iterator i2 = programs.find(programID);
314 2453 : if (i2 != programs.end()) {
315 2437 : return i2->second;
316 : }
317 : }
318 : return nullptr;
319 : }
320 :
321 : const NBTrafficLightLogicCont::Program2Def&
322 7776 : NBTrafficLightLogicCont::getPrograms(const std::string& id) const {
323 : Id2Defs::const_iterator it = myDefinitions.find(id);
324 7776 : if (it != myDefinitions.end()) {
325 7437 : return it->second;
326 : } else {
327 : return EmptyDefinitions;
328 : }
329 : }
330 :
331 :
332 : NBTrafficLightLogic*
333 0 : NBTrafficLightLogicCont::getLogic(const std::string& id, const std::string& programID) const {
334 : Id2Logics::const_iterator i = myComputed.find(id);
335 0 : if (i != myComputed.end()) {
336 : Program2Logic programs = i->second;
337 : Program2Logic::const_iterator i2 = programs.find(programID);
338 0 : if (i2 != programs.end()) {
339 0 : return i2->second;
340 : }
341 : }
342 : return nullptr;
343 : }
344 :
345 :
346 : void
347 1813 : NBTrafficLightLogicCont::setTLControllingInformation(const NBEdgeCont& ec, const NBNodeCont& nc) {
348 1813 : Definitions definitions = getDefinitions();
349 : // set the information about all participants, first
350 5054 : for (Definitions::iterator it = definitions.begin(); it != definitions.end(); it++) {
351 3241 : (*it)->setParticipantsInformation();
352 : }
353 : // clear previous information because tlDefs may have been removed in netedit
354 1813 : ec.clearControllingTLInformation();
355 : // insert the information about the tl-controlling
356 5052 : for (Definitions::iterator it = definitions.begin(); it != definitions.end(); it++) {
357 3240 : (*it)->setTLControllingInformation();
358 : }
359 : // handle rail signals which are not instantiated as normal definitions
360 58902 : for (std::map<std::string, NBNode*>::const_iterator it = nc.begin(); it != nc.end(); it ++) {
361 57090 : NBNode* n = it->second;
362 57090 : if (n->getType() == SumoXMLNodeType::RAIL_SIGNAL || n->getType() == SumoXMLNodeType::RAIL_CROSSING) {
363 1672 : NBOwnTLDef dummy(n->getID(), n, 0, TrafficLightType::STATIC);
364 1672 : dummy.setParticipantsInformation();
365 1672 : dummy.setTLControllingInformation();
366 1672 : n->setCrossingTLIndices(dummy.getID(), (int)dummy.getControlledLinks().size());
367 1672 : n->removeTrafficLight(&dummy);
368 1672 : }
369 : }
370 1813 : }
371 :
372 :
373 : void
374 23 : NBTrafficLightLogicCont::setOpenDriveSignalParameters() {
375 23 : Definitions definitions = getDefinitions();
376 26 : for (NBTrafficLightDefinition* def : getDefinitions()) {
377 : std::map<NBEdge*, std::string> defaultSignalIDs;
378 28 : for (const NBConnection& con : def->getControlledLinks()) {
379 25 : const NBEdge::Connection& c = con.getFrom()->getConnection(con.getFromLane(), con.getTo(), con.getToLane());
380 50 : if (c.hasParameter("signalID")) {
381 75 : defaultSignalIDs[con.getFrom()] = c.getParameter("signalID");
382 100 : def->setParameter("linkSignalID:" + toString(con.getTLIndex()), c.getParameter("signalID"));
383 : }
384 : }
385 : // oftentimes, signals are placed on connecting road but are meant to apply to all connections from the incoming edge
386 28 : for (const NBConnection& con : def->getControlledLinks()) {
387 25 : const NBEdge::Connection& c = con.getFrom()->getConnection(con.getFromLane(), con.getTo(), con.getToLane());
388 50 : if (!c.hasParameter("signalID") && defaultSignalIDs.count(con.getFrom()) != 0) {
389 0 : WRITE_WARNINGF(TL("Guessing signalID for link index % at traffic light '%'."), con.getTLIndex(), def->getID());
390 0 : def->setParameter("linkSignalID:" + toString(con.getTLIndex()), defaultSignalIDs[con.getFrom()]);
391 : }
392 : }
393 23 : }
394 23 : }
395 :
396 :
397 : void
398 0 : NBTrafficLightLogicCont::applyOpenDriveControllers(OptionsCont& oc) {
399 : // collect connections with controllerID which can be controlled together
400 0 : Definitions definitions = getDefinitions();
401 : std::map<std::string, NBTrafficLightDefinition*> defsToGroup;
402 : std::map<std::string, std::map<std::string, std::set<int>>> controllerID2tlIndex;
403 0 : for (NBTrafficLightDefinition* def : definitions) {
404 : bool makeGroups = false;
405 : NBConnectionVector& connections = def->getControlledLinks();
406 : std::string defID = def->getID();
407 :
408 0 : for (NBConnection conn : connections) {
409 0 : std::string controllerID = conn.getFrom()->getConnection(conn.getFromLane(), conn.getTo(), conn.getToLane()).getParameter("controllerID");
410 0 : if (controllerID != "") {
411 0 : if (controllerID2tlIndex.find(defID) == controllerID2tlIndex.end()) {
412 0 : controllerID2tlIndex[defID][controllerID] = std::set<int>({ conn.getTLIndex() });
413 0 : } else if (controllerID2tlIndex[defID].find(controllerID) != controllerID2tlIndex[defID].end()) {
414 0 : controllerID2tlIndex[defID][controllerID].insert(conn.getTLIndex());
415 : } else {
416 0 : controllerID2tlIndex[defID][controllerID] = std::set<int>({ conn.getTLIndex() });
417 : }
418 0 : makeGroups = controllerID2tlIndex[defID][controllerID].size() > 1 || makeGroups;
419 : }
420 0 : }
421 0 : if (makeGroups) {
422 0 : defsToGroup[def->getID()] = def;
423 : }
424 : }
425 : bool same = true;
426 0 : for (NBTrafficLightLogic* computed : getComputed()) {
427 : const std::string computedID = computed->getID();
428 0 : if (defsToGroup.find(computedID) != defsToGroup.end()) {
429 : // remember corresponding tl indices and check if they can be joined
430 : // execute tl index joins if possible
431 0 : const std::vector<NBTrafficLightLogic::PhaseDefinition> phases = computed->getPhases();
432 : std::vector<int> major2minor;
433 0 : for (const auto& indexSet : controllerID2tlIndex[computedID]) {
434 0 : if (indexSet.second.size() < 2) {
435 0 : continue;
436 : }
437 : bool toMinor = false;
438 0 : for (NBTrafficLightLogic::PhaseDefinition phase : phases) {
439 : std::set<int>::iterator it = indexSet.second.begin();
440 0 : char firstChar = phase.state[*it];
441 0 : if (firstChar == LINKSTATE_TL_GREEN_MAJOR) {
442 : firstChar = LINKSTATE_TL_GREEN_MINOR;
443 : }
444 : it++;
445 0 : for (; it != indexSet.second.end(); it++) {
446 0 : const char compareChar = (phase.state[*it] != LINKSTATE_TL_GREEN_MAJOR) ? phase.state[*it] : (char)LINKSTATE_TL_GREEN_MINOR;
447 0 : if (compareChar != firstChar) {
448 : same = false;
449 : break;
450 0 : } else if (phase.state[*it] == LINKSTATE_TL_GREEN_MINOR) {
451 : toMinor = true;
452 : }
453 : }
454 0 : if (!same) {
455 : break;
456 : }
457 0 : }
458 0 : if (toMinor) {
459 0 : major2minor.push_back(*indexSet.second.begin());
460 : }
461 : }
462 0 : if (same) {
463 0 : NBLoadedSUMOTLDef* lDef = dynamic_cast<NBLoadedSUMOTLDef*>(defsToGroup[computedID]);
464 0 : if (lDef == nullptr) {
465 0 : lDef = new NBLoadedSUMOTLDef(*defsToGroup[computedID], *computed);
466 0 : lDef->setParticipantsInformation();
467 0 : for (int index : major2minor) { // update the signal states (G -> g where needed)
468 0 : for (int i = 0; i < (int)phases.size(); i++) {
469 0 : if (phases[i].state[index] == LINKSTATE_TL_GREEN_MAJOR) {
470 0 : lDef->getLogic()->setPhaseState(i, index, LINKSTATE_TL_GREEN_MINOR);
471 : }
472 : }
473 : }
474 : // join signal groups, update the connections
475 : std::vector<int> indexToRemove;
476 0 : for (const auto& indexSet : controllerID2tlIndex[computedID]) {
477 0 : int minIndex = *indexSet.second.begin();
478 0 : for (int index : indexSet.second) {
479 0 : if (index != minIndex) {
480 0 : lDef->replaceIndex(index, minIndex);
481 0 : indexToRemove.push_back(index);
482 : }
483 : }
484 : }
485 : // remove unused indices from signal programs
486 0 : lDef->cleanupStates();
487 0 : for (NBNode* node : lDef->getNodes()) {
488 0 : node->removeTrafficLight(defsToGroup[computedID]);
489 0 : node->addTrafficLight(lDef);
490 : }
491 0 : removeProgram(defsToGroup[computedID]->getID(), defsToGroup[computedID]->getProgramID());
492 0 : insert(lDef);
493 0 : computeSingleLogic(oc, lDef);
494 0 : }
495 : } else { // TODO: try other strategy to build signal groups
496 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());
497 : }
498 : break;
499 0 : }
500 0 : }
501 0 : }
502 :
503 :
504 : NBTrafficLightLogicCont::Logics
505 5824 : NBTrafficLightLogicCont::getComputed() const {
506 : Logics result;
507 10472 : for (Id2Logics::const_iterator it_id = myComputed.begin(); it_id != myComputed.end(); it_id++) {
508 : const Program2Logic& programs = it_id->second;
509 9319 : for (Program2Logic::const_iterator it_prog = programs.begin(); it_prog != programs.end(); it_prog++) {
510 4671 : result.push_back(it_prog->second);
511 : }
512 : }
513 5824 : return result;
514 0 : }
515 :
516 :
517 : NBTrafficLightLogicCont::Definitions
518 5851 : NBTrafficLightLogicCont::getDefinitions() const {
519 : Definitions result;
520 18723 : for (Id2Defs::const_iterator it_id = myDefinitions.begin(); it_id != myDefinitions.end(); it_id++) {
521 : const Program2Def& programs = it_id->second;
522 22907 : for (Program2Def::const_iterator it_prog = programs.begin(); it_prog != programs.end(); it_prog++) {
523 10035 : result.push_back(it_prog->second);
524 : }
525 : }
526 5851 : return result;
527 0 : }
528 :
529 :
530 : void
531 91 : NBTrafficLightLogicCont::rename(NBTrafficLightDefinition* tlDef, const std::string& newID) {
532 : auto it = myDefinitions.find(tlDef->getID());
533 91 : if (it != myDefinitions.end()) {
534 182 : for (auto item : it->second) {
535 91 : item.second->setID(newID);
536 : }
537 91 : myDefinitions[newID] = it->second;
538 : myDefinitions.erase(it);
539 : }
540 : auto it2 = myComputed.find(tlDef->getID());
541 91 : if (it2 != myComputed.end()) {
542 0 : for (auto item : it2->second) {
543 0 : item.second->setID(newID);
544 : }
545 0 : myComputed[newID] = it2->second;
546 : myComputed.erase(it2);
547 : }
548 91 : }
549 :
550 :
551 : int
552 56 : NBTrafficLightLogicCont::getNumExtracted() const {
553 56 : return (int)myExtracted.size();
554 : }
555 :
556 : /****************************************************************************/
|