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 NIXMLTrafficLightsHandler.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Michael Behrisch
17 : /// @author Jakob Erdmann
18 : /// @date 2011-10-05
19 : ///
20 : // Importer for traffic lights stored in XML
21 : /****************************************************************************/
22 : #include <config.h>
23 :
24 : #include <string>
25 : #include <iostream>
26 : #include <utils/common/StringTokenizer.h>
27 : #include <utils/xml/SUMOSAXHandler.h>
28 : #include <utils/xml/SUMOXMLDefinitions.h>
29 : #include <utils/common/ToString.h>
30 : #include <utils/common/StringUtils.h>
31 : #include <utils/common/UtilExceptions.h>
32 : #include <utils/common/MsgHandler.h>
33 : #include <utils/options/OptionsCont.h>
34 : #include <netbuild/NBEdge.h>
35 : #include <netbuild/NBEdgeCont.h>
36 : #include <netbuild/NBNode.h>
37 : #include <netbuild/NBOwnTLDef.h>
38 : #include <netbuild/NBLoadedSUMOTLDef.h>
39 : #include <netbuild/NBTrafficLightLogicCont.h>
40 : #include "NIImporter_SUMO.h"
41 : #include "NIXMLTrafficLightsHandler.h"
42 :
43 :
44 : // ===========================================================================
45 : // method definitions
46 : // ===========================================================================
47 1834 : NIXMLTrafficLightsHandler::NIXMLTrafficLightsHandler(
48 1834 : NBTrafficLightLogicCont& tlCont, NBEdgeCont& ec, bool ignoreUnknown) :
49 : SUMOSAXHandler("xml-tllogics"),
50 1834 : myTLLCont(tlCont),
51 1834 : myEdgeCont(ec),
52 1834 : myCurrentTL(nullptr),
53 1834 : myResetPhases(false),
54 3668 : myIgnoreUnknown(ignoreUnknown)
55 1834 : { }
56 :
57 :
58 1834 : NIXMLTrafficLightsHandler::~NIXMLTrafficLightsHandler() {}
59 :
60 :
61 : void
62 496 : NIXMLTrafficLightsHandler::myStartElement(
63 : int element, const SUMOSAXAttributes& attrs) {
64 496 : switch (element) {
65 44 : case SUMO_TAG_TLLOGIC:
66 44 : myCurrentTL = initTrafficLightLogic(attrs, myCurrentTL);
67 44 : break;
68 236 : case SUMO_TAG_PHASE:
69 236 : if (myCurrentTL != nullptr) {
70 236 : if (myResetPhases) {
71 42 : myCurrentTL->getLogic()->resetPhases();
72 42 : myResetPhases = false;
73 : }
74 236 : NIImporter_SUMO::addPhase(attrs, myCurrentTL);
75 235 : myCurrentTL->phasesLoaded();
76 : }
77 : break;
78 128 : case SUMO_TAG_CONNECTION:
79 128 : addTlConnection(attrs);
80 128 : break;
81 50 : case SUMO_TAG_DEL:
82 50 : removeTlConnection(attrs);
83 50 : break;
84 1 : case SUMO_TAG_PARAM:
85 1 : if (myCurrentTL != nullptr) {
86 1 : bool ok = true;
87 1 : const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
88 : // circumventing empty string test
89 1 : const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
90 1 : myCurrentTL->setParameter(key, val);
91 : }
92 : break;
93 : default:
94 : break;
95 : }
96 495 : }
97 :
98 :
99 : void
100 493 : NIXMLTrafficLightsHandler::myEndElement(int element) {
101 493 : switch (element) {
102 43 : case SUMO_TAG_TLLOGIC:
103 43 : myCurrentTL = nullptr;
104 43 : break;
105 : default:
106 : break;
107 : }
108 493 : }
109 :
110 :
111 : NBLoadedSUMOTLDef*
112 44 : NIXMLTrafficLightsHandler::initTrafficLightLogic(const SUMOSAXAttributes& attrs, NBLoadedSUMOTLDef* currentTL) {
113 44 : if (currentTL) {
114 0 : WRITE_ERRORF(TL("Definition of tlLogic '%' was not finished."), currentTL->getID());
115 0 : return nullptr;
116 : }
117 44 : bool ok = true;
118 44 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
119 88 : std::string programID = attrs.getOpt<std::string>(SUMO_ATTR_PROGRAMID, id.c_str(), ok, "UNKNOWN_PROGRAM");
120 44 : const SUMOTime offset = attrs.getOptOffsetReporting(SUMO_ATTR_OFFSET, id.c_str(), ok, 0);
121 : std::string typeS = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nullptr, ok,
122 88 : OptionsCont::getOptions().getString("tls.default-type"));
123 : TrafficLightType type;
124 : if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
125 44 : type = SUMOXMLDefinitions::TrafficLightTypes.get(typeS);
126 : } else {
127 0 : WRITE_ERRORF(TL("Unknown traffic light type '%' for tlLogic '%'."), typeS, id);
128 0 : return nullptr;
129 : }
130 : // there are three scenarios to consider
131 : // 1) the tll.xml is loaded to update traffic lights defined in a net.xml:
132 : // simply retrieve the loaded definitions and update them
133 : // 2) the tll.xml is loaded to define new traffic lights
134 : // nod.xml will have triggered building of NBOwnTLDef. Replace it with NBLoadedSUMOTLDef
135 : // 3) the tll.xml is loaded to define new programs for a defined traffic light
136 : // there should be a definition with the same id but different programID
137 44 : const std::map<std::string, NBTrafficLightDefinition*>& programs = myTLLCont.getPrograms(id);
138 44 : if (programs.size() == 0) {
139 0 : if (!myIgnoreUnknown) {
140 0 : WRITE_ERRORF(TL("Cannot load traffic light program for unknown id '%', programID '%'."), id, programID);
141 : }
142 0 : return nullptr;
143 : }
144 : const std::string existingProgram = programs.begin()->first; // arbitrary for our purpose
145 44 : NBLoadedSUMOTLDef* loadedDef = dynamic_cast<NBLoadedSUMOTLDef*>(myTLLCont.getDefinition(id, programID));
146 29 : if (loadedDef == nullptr) {
147 33 : NBLoadedSUMOTLDef* oldDef = dynamic_cast<NBLoadedSUMOTLDef*>(myTLLCont.getDefinition(id, existingProgram));
148 33 : if (oldDef == nullptr) {
149 : // case 2
150 22 : NBTrafficLightDefinition* newDef = dynamic_cast<NBOwnTLDef*>(myTLLCont.getDefinition(
151 : id, NBTrafficLightDefinition::DefaultProgramID));
152 : bool deleteDefault = false;
153 22 : if (newDef == nullptr) {
154 : // the default program may have already been replaced with a loaded program
155 0 : newDef = dynamic_cast<NBLoadedSUMOTLDef*>(myTLLCont.getDefinition(
156 : id, NBTrafficLightDefinition::DefaultProgramID));
157 0 : if (newDef == nullptr) {
158 0 : WRITE_ERRORF(TL("Cannot load traffic light program for unknown id '%', programID '%'."), id, programID);
159 0 : return nullptr;
160 : }
161 : } else {
162 : deleteDefault = true;
163 : }
164 : assert(newDef != nullptr);
165 22 : loadedDef = new NBLoadedSUMOTLDef(id, programID, offset, type);
166 : // copy nodes and controlled inner edges
167 44 : for (NBNode* const n : newDef->getNodes()) {
168 22 : loadedDef->addNode(n);
169 : }
170 22 : loadedDef->addControlledInnerEdges(newDef->getControlledInnerEdges());
171 22 : if (deleteDefault) {
172 : // make a copy because the vector is modified in the loop
173 22 : const std::vector<NBNode*> nodes = newDef->getNodes();
174 : // replace default Program
175 44 : for (NBNode* const n : nodes) {
176 22 : n->removeTrafficLight(newDef);
177 : }
178 66 : myTLLCont.removeProgram(id, NBTrafficLightDefinition::DefaultProgramID);
179 22 : }
180 22 : myTLLCont.insert(loadedDef);
181 : } else {
182 : // case 3
183 : NBTrafficLightLogic* oldLogic = oldDef->getLogic();
184 11 : NBTrafficLightLogic newLogic(id, programID, oldLogic->getNumLinks(), offset, type);
185 11 : loadedDef = new NBLoadedSUMOTLDef(*oldDef, newLogic);
186 : // copy nodes
187 11 : std::vector<NBNode*> nodes = oldDef->getNodes();
188 23 : for (std::vector<NBNode*>::iterator it = nodes.begin(); it != nodes.end(); it++) {
189 12 : loadedDef->addNode(*it);
190 : }
191 : //std::cout << " case3 oldDef=" << oldDef->getDescription() << " loadedDef=" << loadedDef->getDescription() << "\n";
192 11 : myTLLCont.insert(loadedDef);
193 11 : }
194 : } else {
195 : // case 1
196 11 : if (attrs.hasAttribute(SUMO_ATTR_OFFSET)) {
197 6 : loadedDef->setOffset(offset);
198 : }
199 11 : if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
200 6 : loadedDef->setType(type);
201 : }
202 : }
203 44 : if (ok) {
204 44 : myResetPhases = true;
205 : mySeenIDs.insert(id);
206 : return loadedDef;
207 : } else {
208 : return nullptr;
209 : }
210 : }
211 :
212 :
213 : void
214 128 : NIXMLTrafficLightsHandler::addTlConnection(const SUMOSAXAttributes& attrs) {
215 128 : bool ok = true;
216 : // parse identifying attributes
217 128 : NBEdge* from = retrieveEdge(attrs, SUMO_ATTR_FROM, ok);
218 128 : NBEdge* to = retrieveEdge(attrs, SUMO_ATTR_TO, ok);
219 128 : if (!ok) {
220 6 : return;
221 : }
222 128 : int fromLane = retrieveLaneIndex(attrs, SUMO_ATTR_FROM_LANE, from, ok);
223 128 : int toLane = retrieveLaneIndex(attrs, SUMO_ATTR_TO_LANE, to, ok);
224 128 : if (!ok) {
225 : return;
226 : }
227 : // retrieve connection
228 : const std::vector<NBEdge::Connection>& connections = from->getConnections();
229 : std::vector<NBEdge::Connection>::const_iterator con_it;
230 128 : con_it = find_if(connections.begin(), connections.end(),
231 : NBEdge::connections_finder(fromLane, to, toLane));
232 128 : if (con_it == connections.end()) {
233 30 : WRITE_ERROR("Connection from=" + from->getID() + " to=" + to->getID() +
234 : " fromLane=" + toString(fromLane) + " toLane=" + toString(toLane) + " not found");
235 6 : return;
236 : }
237 122 : NBEdge::Connection c = *con_it;
238 : // read other attributes
239 122 : std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, "");
240 122 : if (tlID == "") {
241 : // we are updating an existing tl-controlled connection
242 8 : tlID = (*(from->getToNode()->getControllingTLS().begin()))->getID();
243 : assert(tlID != "");
244 : }
245 122 : int tlIndex = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok, -1);
246 122 : if (tlIndex == -1) {
247 : // we are updating an existing tl-controlled connection
248 0 : tlIndex = c.tlLinkIndex;
249 : }
250 122 : int tlIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX2, nullptr, ok, -1);
251 122 : if (tlIndex2 == -1) {
252 : // we are updating an existing tl-controlled connection or index2 is not used
253 122 : tlIndex2 = c.tlLinkIndex2;
254 : }
255 :
256 : // register the connection with all definitions
257 122 : const std::map<std::string, NBTrafficLightDefinition*>& programs = myTLLCont.getPrograms(tlID);
258 122 : if (programs.size() > 0) {
259 : std::map<std::string, NBTrafficLightDefinition*>::const_iterator it;
260 253 : for (it = programs.begin(); it != programs.end(); it++) {
261 131 : NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(it->second);
262 131 : if (tlDef) {
263 131 : tlDef->addConnection(from, c.toEdge, c.fromLane, c.toLane, tlIndex, tlIndex2, false);
264 : } else {
265 : throw ProcessError("Corrupt traffic light definition '"
266 0 : + tlID + "' (program '" + it->first + "')");
267 : }
268 : }
269 : } else {
270 : SumoXMLNodeType type = from->getToNode()->getType();
271 0 : if (type != SumoXMLNodeType::RAIL_CROSSING && type != SumoXMLNodeType::RAIL_SIGNAL) {
272 0 : WRITE_ERRORF(TL("The traffic light '%' is not known."), tlID);
273 : }
274 : }
275 122 : }
276 :
277 :
278 : void
279 50 : NIXMLTrafficLightsHandler::removeTlConnection(const SUMOSAXAttributes& attrs) {
280 50 : bool ok = true;
281 50 : std::string tlID = attrs.get<std::string>(SUMO_ATTR_TLID, nullptr, ok);
282 : // does the traffic light still exist?
283 50 : const std::map<std::string, NBTrafficLightDefinition*>& programs = myTLLCont.getPrograms(tlID);
284 50 : if (programs.size() > 0) {
285 : // parse identifying attributes
286 31 : NBEdge* from = retrieveEdge(attrs, SUMO_ATTR_FROM, ok);
287 31 : NBEdge* to = retrieveEdge(attrs, SUMO_ATTR_TO, ok);
288 : int fromLane = -1;
289 : int toLane = -1;
290 31 : if (ok) {
291 31 : fromLane = retrieveLaneIndex(attrs, SUMO_ATTR_FROM_LANE, from, ok, true);
292 31 : toLane = retrieveLaneIndex(attrs, SUMO_ATTR_TO_LANE, to, ok, true);
293 : }
294 31 : int tlIndex = attrs.get<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok);
295 :
296 31 : NBConnection conn(from, fromLane, to, toLane, tlIndex);
297 : // remove the connection from all definitions
298 : std::map<std::string, NBTrafficLightDefinition*>::const_iterator it;
299 62 : for (it = programs.begin(); it != programs.end(); it++) {
300 31 : NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(it->second);
301 31 : if (tlDef) {
302 31 : tlDef->removeConnection(conn, false);
303 : } else {
304 : throw ProcessError("Corrupt traffic light definition '"
305 0 : + tlID + "' (program '" + it->first + "')");
306 : }
307 : }
308 31 : }
309 50 : }
310 :
311 :
312 : NBEdge*
313 318 : NIXMLTrafficLightsHandler::retrieveEdge(
314 : const SUMOSAXAttributes& attrs, SumoXMLAttr attr, bool& ok) {
315 318 : std::string edgeID = attrs.get<std::string>(attr, nullptr, ok);
316 318 : NBEdge* edge = myEdgeCont.retrieve(edgeID, true);
317 318 : if (edge == nullptr) {
318 0 : WRITE_ERRORF(TL("Unknown edge '%' given in connection."), edgeID);
319 0 : ok = false;
320 : }
321 318 : return edge;
322 : }
323 :
324 :
325 : int
326 318 : NIXMLTrafficLightsHandler::retrieveLaneIndex(
327 : const SUMOSAXAttributes& attrs, SumoXMLAttr attr, NBEdge* edge, bool& ok, bool isDelete) {
328 318 : int laneIndex = attrs.get<int>(attr, nullptr, ok);
329 318 : if (edge->getNumLanes() <= laneIndex) {
330 0 : if (!isDelete) {
331 0 : WRITE_ERRORF(TL("Invalid lane index '%' for edge '%'."), toString(laneIndex), edge->getID());
332 : }
333 0 : ok = false;
334 : }
335 318 : return laneIndex;
336 : }
337 :
338 :
339 : /****************************************************************************/
|