Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 NLTriggerBuilder.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Tino Morenz
17 : /// @author Jakob Erdmann
18 : /// @author Eric Nicolay
19 : /// @author Sascha Krieg
20 : /// @author Michael Behrisch
21 : /// @author Johannes Rummel
22 : /// @date Thu, 17 Oct 2002
23 : ///
24 : // Builds trigger objects for microsim
25 : /****************************************************************************/
26 : #include <config.h>
27 :
28 : #include <string>
29 : #include <mesosim/MELoop.h>
30 : #include <mesosim/METriggeredCalibrator.h>
31 : #include <microsim/MSEventControl.h>
32 : #include <microsim/MSJunctionControl.h>
33 : #include <microsim/MSLane.h>
34 : #include <microsim/MSEdge.h>
35 : #include <microsim/MSGlobals.h>
36 : #include <microsim/MSParkingArea.h>
37 : #include <microsim/MSStoppingPlace.h>
38 : #include <microsim/output/MSDetectorControl.h>
39 : #include <microsim/output/MSRouteProbe.h>
40 : #include <microsim/trigger/MSLaneSpeedTrigger.h>
41 : #include <microsim/trigger/MSTriggeredRerouter.h>
42 : #include <microsim/trigger/MSCalibrator.h>
43 : #include <microsim/trigger/MSChargingStation.h>
44 : #include <microsim/trigger/MSOverheadWire.h>
45 : #include <utils/common/StringTokenizer.h>
46 : #include <utils/common/FileHelpers.h>
47 : #include <utils/common/UtilExceptions.h>
48 : #include <utils/common/WrappingCommand.h>
49 : #include <utils/common/RGBColor.h>
50 : #include <utils/options/OptionsCont.h>
51 : #include <utils/xml/SUMOXMLDefinitions.h>
52 : #include <utils/xml/XMLSubSys.h>
53 : #include "NLHandler.h"
54 : #include "NLTriggerBuilder.h"
55 :
56 :
57 : // ===========================================================================
58 : // method definitions
59 : // ===========================================================================
60 41234 : NLTriggerBuilder::NLTriggerBuilder()
61 41234 : : myHandler(nullptr), myParkingArea(nullptr), myCurrentStop(nullptr) {}
62 :
63 :
64 41234 : NLTriggerBuilder::~NLTriggerBuilder() {}
65 :
66 : void
67 41234 : NLTriggerBuilder::setHandler(NLHandler* handler) {
68 41234 : myHandler = handler;
69 41234 : }
70 :
71 :
72 : void
73 505 : NLTriggerBuilder::buildVaporizer(const SUMOSAXAttributes& attrs) {
74 505 : WRITE_WARNING(TL("Vaporizers are deprecated. Use rerouters instead."));
75 505 : bool ok = true;
76 : // get the id, throw if not given or empty...
77 505 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
78 505 : if (!ok) {
79 : return;
80 : }
81 493 : MSEdge* e = MSEdge::dictionary(id);
82 493 : if (e == nullptr) {
83 18 : WRITE_ERRORF(TL("Unknown edge ('%') referenced in a vaporizer."), id);
84 6 : return;
85 : }
86 487 : SUMOTime begin = attrs.getSUMOTimeReporting(SUMO_ATTR_BEGIN, nullptr, ok);
87 487 : SUMOTime end = attrs.getSUMOTimeReporting(SUMO_ATTR_END, nullptr, ok);
88 487 : if (!ok) {
89 : return;
90 : }
91 451 : if (begin < 0) {
92 18 : WRITE_ERRORF(TL("A vaporization begin time is negative (edge id='%')."), id);
93 6 : return;
94 : }
95 445 : if (begin >= end) {
96 36 : WRITE_ERRORF(TL("A vaporization ends before it starts (edge id='%')."), id);
97 12 : return;
98 : }
99 866 : if (end >= string2time(OptionsCont::getOptions().getString("begin"))) {
100 433 : Command* cb = new WrappingCommand< MSEdge >(e, &MSEdge::incVaporization);
101 433 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(cb, begin);
102 433 : Command* ce = new WrappingCommand< MSEdge >(e, &MSEdge::decVaporization);
103 433 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(ce, end);
104 : }
105 : }
106 :
107 :
108 : void
109 503 : NLTriggerBuilder::parseAndBuildLaneSpeedTrigger(MSNet& net, const SUMOSAXAttributes& attrs,
110 : const std::string& base) {
111 : // get the id, throw if not given or empty...
112 503 : bool ok = true;
113 : // get the id, throw if not given or empty...
114 503 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
115 503 : if (!ok) {
116 : return;
117 : }
118 : // get the file name to read further definitions from
119 503 : std::string file = getFileName(attrs, base, true);
120 503 : std::string objectid = attrs.get<std::string>(SUMO_ATTR_LANES, id.c_str(), ok);
121 : std::vector<MSLane*> lanes;
122 1060 : for (const std::string& laneID : attrs.get<std::vector<std::string> >(SUMO_ATTR_LANES, id.c_str(), ok)) {
123 557 : MSLane* lane = MSLane::dictionary(laneID);
124 557 : if (lane == nullptr) {
125 0 : throw InvalidArgument("The lane '" + laneID + "' to use within MSLaneSpeedTrigger '" + id + "' is not known.");
126 : }
127 557 : lanes.push_back(lane);
128 503 : }
129 503 : if (!ok) {
130 0 : throw InvalidArgument("The lanes to use within MSLaneSpeedTrigger '" + id + "' are not known.");
131 : }
132 503 : if (lanes.size() == 0) {
133 0 : throw InvalidArgument("No lane defined for MSLaneSpeedTrigger '" + id + "'.");
134 : }
135 : try {
136 503 : MSLaneSpeedTrigger* trigger = buildLaneSpeedTrigger(net, id, lanes, file);
137 503 : if (file == "") {
138 335 : trigger->registerParent(SUMO_TAG_VSS, myHandler);
139 : }
140 0 : } catch (ProcessError& e) {
141 0 : throw InvalidArgument(e.what());
142 0 : }
143 503 : }
144 :
145 :
146 : void
147 15241 : NLTriggerBuilder::parseAndBuildChargingStation(MSNet& net, const SUMOSAXAttributes& attrs) {
148 15241 : bool ok = true;
149 :
150 : // get the id, throw if not given or empty...
151 15241 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
152 15241 : if (!ok) {
153 0 : throw ProcessError();
154 : }
155 :
156 30482 : MSLane* const lane = getLane(attrs, "chargingStation", id);
157 15241 : double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
158 15241 : double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
159 15241 : const double chargingPower = attrs.getOpt<double>(SUMO_ATTR_CHARGINGPOWER, id.c_str(), ok, 22000);
160 15241 : const double totalPower = attrs.getOpt<double>(SUMO_ATTR_TOTALPOWER, id.c_str(), ok, -1);
161 15241 : const double efficiency = attrs.getOpt<double>(SUMO_ATTR_EFFICIENCY, id.c_str(), ok, 0.95);
162 15241 : const bool chargeInTransit = attrs.getOpt<bool>(SUMO_ATTR_CHARGEINTRANSIT, id.c_str(), ok, 0);
163 15241 : const SUMOTime chargeDelay = attrs.getOptSUMOTimeReporting(SUMO_ATTR_CHARGEDELAY, id.c_str(), ok, 0);
164 30483 : const std::string chargeType = attrs.getOpt<std::string>(SUMO_ATTR_CHARGETYPE, id.c_str(), ok, "normal");
165 15241 : const SUMOTime waitingTime = attrs.getOptSUMOTimeReporting(SUMO_ATTR_WAITINGTIME, id.c_str(), ok, 900);
166 15241 : const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
167 15242 : const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
168 30482 : MSParkingArea* parkingArea = getParkingArea(attrs, "parkingArea", id);
169 :
170 : // check charge type
171 15241 : if ((chargeType != "normal") && (chargeType != "battery-exchange") && (chargeType != "fuel")) {
172 0 : throw InvalidArgument("The chargeType to use within MSLaneSpeedTrigger '" + id + "' is invalid.");
173 : }
174 :
175 15241 : if (!ok || (myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
176 3 : throw InvalidArgument("Invalid position for charging station '" + id + "'.");
177 : }
178 :
179 45721 : buildChargingStation(net, id, lane, frompos, topos, name, chargingPower, totalPower, efficiency, chargeInTransit, chargeDelay, chargeType, waitingTime, parkingArea);
180 15240 : }
181 :
182 :
183 : void
184 58 : NLTriggerBuilder::parseAndBuildOverheadWireSegment(MSNet& net, const SUMOSAXAttributes& attrs) {
185 58 : bool ok = true;
186 :
187 : // get the id, throw if not given or empty...
188 58 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
189 58 : if (!ok) {
190 0 : throw ProcessError();
191 : }
192 :
193 : /* The following call may either throw InvalidArgument exeption or return NULL:
194 : NULL is returned in case when the overhead wire segment should be built over an already
195 : ignored internal lane of an intersection, the exeption is thrown in case that
196 : the overhead wire segment references a non-existent lane. */
197 58 : MSLane* const lane = getLane(attrs, "overheadWireSegment", id);
198 58 : if (lane == nullptr) {
199 0 : WRITE_MESSAGEF(TL("The overheadWireSegment '%' was not created as it is attached to internal lane. It will be build automatically."), id);
200 0 : return;
201 : }
202 :
203 58 : if (lane->isInternal()) {
204 0 : WRITE_MESSAGEF(TL("The overheadWireSegment '%' not built as it is attached to internal lane. It will be build automatically."), id);
205 0 : return;
206 : }
207 :
208 58 : double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
209 58 : double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
210 58 : const bool voltageSource = attrs.getOpt<bool>(SUMO_ATTR_VOLTAGESOURCE, id.c_str(), ok, false);
211 58 : const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
212 :
213 58 : if (!ok || myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID) {
214 0 : frompos = 0;
215 0 : topos = lane->getLength();
216 0 : WRITE_MESSAGEF(TL("The overheadWireSegment '%' has wrong position. Automatically set from 0 to the length of the lane."), id);
217 : //throw InvalidArgument("Invalid position for overheadWireSegment'" + id + "'.");
218 : }
219 :
220 58 : buildOverheadWireSegment(net, id, lane, frompos, topos, voltageSource);
221 : #ifndef HAVE_EIGEN
222 : if (MSGlobals::gOverheadWireSolver && !myHaveWarnedAboutEigen) {
223 : myHaveWarnedAboutEigen = true;
224 : WRITE_WARNING(TL("Overhead wire solver (Eigen) not compiled in, expect errors in overhead wire simulation"))
225 : }
226 : #endif // !HAVE_EIGEN
227 : }
228 :
229 : void
230 8 : NLTriggerBuilder::parseAndBuildOverheadWireSection(MSNet& net, const SUMOSAXAttributes& attrs) {
231 8 : bool ok = true;
232 8 : std::string substationId = attrs.get<std::string>(SUMO_ATTR_SUBSTATIONID, 0, ok);
233 8 : if (!ok) {
234 0 : throw ProcessError();
235 : }
236 :
237 8 : MSTractionSubstation* substation = MSNet::getInstance()->findTractionSubstation(substationId);
238 8 : if (substation == nullptr) {
239 0 : throw InvalidArgument("Traction substation '" + substationId + "' refereced by an OverheadWire Section is not known.");
240 8 : } else if (substation->isAnySectionPreviouslyDefined()) {
241 0 : throw InvalidArgument("Traction substation '" + substationId + "' refereced by an OverheadWire Section is probably referenced twice (a known limitation of the actual version of overhead wire simulation).");
242 : }
243 :
244 : // @todo This may be a relict of older approach to processing the attributes ...
245 8 : std::string segmentStrings = attrs.get<std::string>(SUMO_ATTR_OVERHEAD_WIRE_SEGMENTS, substationId.c_str(), ok);
246 8 : if (!ok) {
247 0 : throw InvalidArgument("Segments referenced by Traction substation '" + substationId + "' are not declared .");
248 : }
249 :
250 : // process forbidden internal lanes
251 8 : const std::vector<std::string>& forbiddenInnerLanesIDs = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_OVERHEAD_WIRE_FORBIDDEN, substationId.c_str(), ok);
252 : /// @todo for cycle abbreviation?
253 8 : for (const std::string& laneID : forbiddenInnerLanesIDs) {
254 0 : MSLane* lane = MSLane::dictionary(laneID);
255 0 : if (lane != nullptr) {
256 0 : substation->addForbiddenLane(lane);
257 : } else {
258 0 : throw InvalidArgument("Unknown lane '" + laneID + "' in Traction substation '" + substationId + "'.");
259 : }
260 : }
261 :
262 : // @todo Check this as well ...
263 : // Original version from 2018
264 : // std::vector<std::string> segmentIDs;
265 : // SUMOSAXAttributes::parseStringVector(segmentStrings, segmentIDs);
266 8 : const std::vector<std::string>& segmentIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_OVERHEAD_WIRE_SEGMENTS, substationId.c_str(), ok);
267 : std::vector<MSOverheadWire*> segments;
268 :
269 : // ----------------------------------------------
270 : // Add overhead wire segments over internal lanes
271 : // ----------------------------------------------
272 :
273 : // Adding internal overhead wire segments (segments on neighboring inner lanes if a connection between two regular lane with overhead wire segment exists)
274 46 : for (const std::string& segmentID : segmentIDs) {
275 : const MSLane* connection = nullptr;
276 38 : MSOverheadWire* ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(segmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
277 : std::string neigboringOvrhdSegmentID;
278 : MSOverheadWire* neigboringOvrhdSegment;
279 : MSTractionSubstation* neigboringOvrhdSegmentTractionSubstation;
280 38 : if (ovrhdSegment == nullptr) {
281 0 : throw InvalidArgument("The OverheadWireSegment with id='" + segmentID + "' referenced by OverheadWireSegment for substation '" + substationId + "' was not defined.");
282 : }
283 :
284 : MSTractionSubstation* ts = ovrhdSegment->getTractionSubstation();
285 38 : if (!(ts == substation || ts == nullptr)) {
286 : std::string tsName = ts->getID();
287 0 : throw InvalidArgument("The OverheadWireSegment '" + segmentID + "' referenced by OverheadWireSegment for substation '" + substationId + "' is already assigned to substation '" + tsName + "'.");
288 : }
289 : ovrhdSegment->setTractionSubstation(substation);
290 :
291 38 : const MSLane* lane = &(ovrhdSegment->getLane());
292 :
293 : /* in version before SUMO 1.0.1 the function getOutgoingLanes() returning MSLane* exists,
294 : in new version of SUMO the funciton getOutgoingViaLanes() returning MSLane* and MSEdge* pair exists */
295 38 : const std::vector<std::pair<const MSLane*, const MSEdge*> > outgoingLanesAndEdges = lane->getOutgoingViaLanes();
296 : std::vector<const MSLane*> neigboringInnerLanes;
297 38 : neigboringInnerLanes.reserve(outgoingLanesAndEdges.size());
298 89 : for (size_t it = 0; it < outgoingLanesAndEdges.size(); ++it) {
299 51 : neigboringInnerLanes.push_back(outgoingLanesAndEdges[it].first);
300 : }
301 :
302 : // Check if an outgoing lane has an overhead wire segment. If not, do nothing, otherwise find connnecting internal lanes and
303 : // add overhead wire segments over all detected internal lanes
304 89 : for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
305 : // If the overhead wire segment is over the outgoing (not internal) lane
306 102 : neigboringOvrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
307 51 : if (neigboringOvrhdSegmentID != "") {
308 25 : neigboringOvrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(neigboringOvrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
309 : neigboringOvrhdSegmentTractionSubstation = neigboringOvrhdSegment->getTractionSubstation();
310 : } else {
311 : neigboringOvrhdSegment = nullptr;
312 : neigboringOvrhdSegmentTractionSubstation = nullptr;
313 : }
314 :
315 51 : if (neigboringOvrhdSegmentTractionSubstation == substation && !(*it)->isInternal()) {
316 2 : connection = lane->getInternalFollowingLane(*it);
317 2 : if (connection != nullptr) {
318 : //is connection forbidden?
319 2 : if (!(substation->isForbidden(connection) || substation->isForbidden(lane->getInternalFollowingLane(connection)) || substation->isForbidden(connection->getInternalFollowingLane(*it)))) {
320 2 : buildInnerOverheadWireSegments(net, connection, lane->getInternalFollowingLane(connection), connection->getInternalFollowingLane(*it));
321 : }
322 : }
323 : }
324 : }
325 :
326 : // Check if an incoming lane has an overhead wire segment. If not, do nothing, otherwise find connnecting internal lanes and
327 : // add overhead wire segments over all detected internal lanes
328 38 : neigboringInnerLanes = lane->getNormalIncomingLanes();
329 111 : for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
330 : // If the overhead wire segment is over the incoming (not internal) lane
331 146 : neigboringOvrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, (*it)->getLength() - NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
332 73 : if (neigboringOvrhdSegmentID != "") {
333 19 : neigboringOvrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(neigboringOvrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
334 : neigboringOvrhdSegmentTractionSubstation = neigboringOvrhdSegment->getTractionSubstation();
335 : } else {
336 : neigboringOvrhdSegment = nullptr;
337 : neigboringOvrhdSegmentTractionSubstation = nullptr;
338 : }
339 :
340 73 : if (neigboringOvrhdSegmentTractionSubstation == substation && !(*it)->isInternal()) {
341 17 : connection = (*it)->getInternalFollowingLane(lane);
342 17 : if (connection != nullptr) {
343 : //is connection forbidden?
344 2 : if (!(substation->isForbidden(connection) || substation->isForbidden((*it)->getInternalFollowingLane(connection)) || substation->isForbidden(connection->getInternalFollowingLane(lane)))) {
345 2 : buildInnerOverheadWireSegments(net, connection, (*it)->getInternalFollowingLane(connection), connection->getInternalFollowingLane(lane));
346 : }
347 : }
348 : }
349 : }
350 38 : }
351 :
352 :
353 : // ----- *** adding segments into the electric circuit*** -----
354 :
355 : // setting nullptr for substation (a fragment from old version of adding segments into the circuit)
356 46 : for (const std::string& segmentID : segmentIDs) {
357 38 : MSOverheadWire* ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(segmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
358 : ovrhdSegment->setTractionSubstation(nullptr);
359 : }
360 :
361 46 : for (const std::string& segmentID : segmentIDs) {
362 38 : if (segmentID == "") {
363 0 : continue;
364 : }
365 38 : MSOverheadWire* ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(segmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
366 38 : substation->addOverheadWireSegmentToCircuit(ovrhdSegment);
367 38 : segments.push_back(ovrhdSegment);
368 : }
369 :
370 : // adding overhead wire clamp
371 16 : std::string clampsString = attrs.getOpt<std::string>(SUMO_ATTR_OVERHEAD_WIRE_CLAMPS, nullptr, ok, "");
372 8 : if (clampsString != "" && MSGlobals::gOverheadWireSolver) {
373 : #ifdef HAVE_EIGEN
374 5 : const std::vector<std::string>& clampIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_OVERHEAD_WIRE_CLAMPS, nullptr, ok);
375 16 : for (const std::string& clampID : clampIDs) {
376 11 : MSTractionSubstation::OverheadWireClamp* clamp = substation->findClamp(clampID);
377 11 : if (clamp != nullptr) {
378 11 : if (clamp->start->getTractionSubstation() == substation && clamp->end->getTractionSubstation() == substation) {
379 11 : substation->addOverheadWireClampToCircuit(clamp->id, clamp->start, clamp->end);
380 11 : buildOverheadWireClamp(net, clamp->id, const_cast<MSLane*>(&clamp->start->getLane()), const_cast<MSLane*>(&clamp->end->getLane()));
381 11 : clamp->usage = true;
382 : } else {
383 0 : if (clamp->start->getTractionSubstation() != substation) {
384 0 : WRITE_WARNINGF(TL("A connecting overhead wire start segment '%' defined for overhead wire clamp '%' is not assigned to the traction substation '%'."), clamp->start->getID(), clampID, substationId);
385 : } else {
386 0 : WRITE_WARNINGF(TL("A connecting overhead wire end segment '%' defined for overhead wire clamp '%' is not assigned to the traction substation '%'."), clamp->end->getID(), clampID, substationId);
387 : }
388 : }
389 : } else {
390 0 : WRITE_WARNINGF(TL("The overhead wire clamp '%' defined in an overhead wire section was not assigned to the substation '%'. Please define proper <overheadWireClamp .../> in additional files before defining overhead wire section."), clampID, substationId);
391 : }
392 : }
393 : #else
394 : WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
395 : #endif
396 5 : }
397 :
398 8 : if (segments.size() == 0) {
399 0 : throw InvalidArgument("No segments found for overHeadWireSection '" + substationId + "'.");
400 8 : } else if (MSGlobals::gOverheadWireSolver) {
401 : #ifdef HAVE_EIGEN
402 : // check that the electric circuit makes sense
403 24 : segments[0]->getCircuit()->checkCircuit(substationId);
404 : #else
405 : WRITE_WARNING(TL("Cannot check circuit, overhead circuit solver support (Eigen) not compiled in."));
406 : #endif
407 : }
408 16 : }
409 :
410 : void
411 17 : NLTriggerBuilder::parseAndBuildTractionSubstation(MSNet& net, const SUMOSAXAttributes& attrs) {
412 17 : bool ok = true;
413 :
414 : // get the id, throw if not given or empty...
415 17 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
416 17 : if (!ok) {
417 0 : throw ProcessError();
418 : }
419 :
420 : // RICE_TODO Limits are fixed, change them to some predefined constants ...
421 17 : const double voltage = attrs.getOpt<double>(SUMO_ATTR_VOLTAGE, id.c_str(), ok, 600);
422 17 : const double currentLimit = attrs.getOpt<double>(SUMO_ATTR_CURRENTLIMIT, id.c_str(), ok, 400);
423 34 : buildTractionSubstation(net, id, voltage, currentLimit);
424 17 : }
425 :
426 : void
427 11 : NLTriggerBuilder::parseAndBuildOverheadWireClamp(MSNet& /*net*/, const SUMOSAXAttributes& attrs) {
428 11 : if (MSGlobals::gOverheadWireSolver) {
429 : #ifdef HAVE_EIGEN
430 11 : bool ok = true;
431 11 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
432 11 : if (!ok) {
433 0 : throw ProcessError();
434 : }
435 :
436 11 : std::string substationId = attrs.get<std::string>(SUMO_ATTR_SUBSTATIONID, 0, ok);
437 11 : if (!ok) {
438 0 : throw ProcessError();
439 : }
440 11 : MSTractionSubstation* substation = MSNet::getInstance()->findTractionSubstation(substationId);
441 11 : if (substation == nullptr) {
442 0 : throw InvalidArgument("Traction substation '" + substationId + "' using within an overheadWireClamp '" + id + "' is not known.");
443 : }
444 :
445 11 : std::string overhead_fromItsStart = attrs.get<std::string>(SUMO_ATTR_OVERHEAD_WIRE_CLAMP_START, 0, ok);
446 11 : if (!ok) {
447 0 : throw ProcessError();
448 : }
449 11 : MSOverheadWire* ovrhdSegment_fromItsStart = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(overhead_fromItsStart, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
450 11 : if (ovrhdSegment_fromItsStart == nullptr) {
451 0 : throw InvalidArgument("The overheadWireSegment '" + overhead_fromItsStart + "' to use within overheadWireClamp '" + id + "' is not known.");
452 : }
453 : /*if (ovrhdSegment_fromItsStart->getTractionSubstation() != substation) {
454 : throw InvalidArgument("The overheadWireSegment '" + overhead_fromItsStart + "' to use within overheadWireClamp is assign to a different overhead wire section or substation.");
455 : }
456 : */
457 11 : std::string overhead_fromItsEnd = attrs.get<std::string>(SUMO_ATTR_OVERHEAD_WIRE_CLAMP_END, 0, ok);
458 11 : if (!ok) {
459 0 : throw ProcessError();
460 : }
461 11 : MSOverheadWire* ovrhdSegment_fromItsEnd = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(overhead_fromItsEnd, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
462 11 : if (ovrhdSegment_fromItsEnd == nullptr) {
463 0 : throw InvalidArgument("The overheadWireSegment '" + overhead_fromItsEnd + "' to use within overheadWireClamp '" + id + "' is not known.");
464 : }
465 : /*
466 : if (ovrhdSegment_fromItsEnd->getTractionSubstation() != substation) {
467 : throw InvalidArgument("The overheadWireSegment '" + overhead_fromItsEnd + "' to use within overheadWireClamp is assign to a different overhead wire section or substation.");
468 : }
469 : */
470 22 : if (substation->findClamp(id) == nullptr) {
471 11 : substation->addClamp(id, ovrhdSegment_fromItsStart, ovrhdSegment_fromItsEnd);
472 : } else {
473 0 : WRITE_ERROR("The overhead wire clamp '" + id + "' is probably declared twice.")
474 : }
475 : #else
476 : UNUSED_PARAMETER(attrs);
477 : WRITE_WARNING(TL("Not building overhead wire clamps, overhead wire solver support (Eigen) not compiled in."));
478 : #endif
479 : } else {
480 0 : WRITE_WARNING(TL("Ignoring overhead wire clamps, they make no sense when overhead wire circuit solver is off."));
481 : }
482 11 : }
483 :
484 :
485 : void
486 50889 : NLTriggerBuilder::parseAndBuildStoppingPlace(MSNet& net, const SUMOSAXAttributes& attrs, const SumoXMLTag element) {
487 50889 : bool ok = true;
488 : // get the id, throw if not given or empty...
489 50889 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
490 50889 : if (!ok) {
491 0 : throw ProcessError();
492 : }
493 :
494 : //get the name, leave blank if not given
495 101789 : const std::string ptStopName = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
496 :
497 : //get the color, use default if not given
498 : // default color, copy from GUIVisualizationStoppingPlaceSettings::busStopColor / containerStopColor
499 50889 : RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok, RGBColor::INVISIBLE);
500 :
501 101778 : MSLane* lane = getLane(attrs, toString(element), id);
502 : // get the positions
503 50889 : double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0.);
504 50889 : double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
505 50889 : double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, 90);
506 50889 : const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
507 50889 : if (!ok || (myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
508 9 : throw InvalidArgument("Invalid position for " + toString(element) + " '" + id + "'.");
509 : }
510 50897 : const std::vector<std::string>& lines = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LINES, id.c_str(), ok);
511 : int defaultCapacity;
512 : SumoXMLAttr capacityAttr;
513 50886 : if (element != SUMO_TAG_CONTAINER_STOP) {
514 37350 : defaultCapacity = MAX2(MSStoppingPlace::getDefaultTransportablesAbreast(topos - frompos, element) * 3, 6);
515 : capacityAttr = SUMO_ATTR_PERSON_CAPACITY;
516 : } else {
517 13536 : defaultCapacity = MSStoppingPlace::getDefaultTransportablesAbreast(topos - frompos, element);
518 : capacityAttr = SUMO_ATTR_CONTAINER_CAPACITY;
519 : }
520 50886 : const int transportableCapacity = attrs.getOpt<int>(capacityAttr, id.c_str(), ok, defaultCapacity);
521 50886 : const double parkingLength = attrs.getOpt<double>(SUMO_ATTR_PARKING_LENGTH, id.c_str(), ok, 0);
522 : // build the bus stop
523 203552 : buildStoppingPlace(net, id, lines, lane, frompos, topos, element, ptStopName, transportableCapacity, parkingLength, color, angle);
524 101764 : }
525 :
526 :
527 : void
528 2252 : NLTriggerBuilder::addAccess(MSNet& /* net */, const SUMOSAXAttributes& attrs) {
529 2252 : if (myCurrentStop == nullptr) {
530 30 : throw InvalidArgument("Could not add access outside a stopping place.");
531 : }
532 : // get the lane
533 4474 : MSLane* lane = getLane(attrs, "access", myCurrentStop->getID());
534 2237 : if (!lane->allowsVehicleClass(SVC_PEDESTRIAN)) {
535 3 : WRITE_WARNINGF(TL("Ignoring invalid access from non-pedestrian lane '%' in busStop '%'."), lane->getID(), myCurrentStop->getID());
536 1 : return;
537 : }
538 : // get the positions
539 2236 : bool ok = true;
540 2236 : const std::string accessPos = attrs.getOpt<std::string>(SUMO_ATTR_POSITION, "access", ok);
541 2236 : const bool random = accessPos == "random";
542 : MSStoppingPlace::AccessExit exit = MSStoppingPlace::AccessExit::PLATFORM;
543 2236 : if (accessPos == "doors") {
544 : exit = MSStoppingPlace::AccessExit::DOORS;
545 2204 : } else if (accessPos == "carriage") {
546 : exit = MSStoppingPlace::AccessExit::CARRIAGE;
547 : }
548 2236 : double startPos = random || exit != MSStoppingPlace::AccessExit::PLATFORM ? 0. : attrs.getOpt<double>(SUMO_ATTR_POSITION, "access", ok, 0);
549 2236 : double endPos = random || exit != MSStoppingPlace::AccessExit::PLATFORM ? lane->getLength() : startPos;
550 2236 : const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, "access", ok, -1);
551 2236 : const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, "access", ok, false);
552 2236 : if (!ok || (myHandler->checkStopPos(startPos, endPos, lane->getLength(), 0, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
553 0 : throw InvalidArgument("Invalid position " + attrs.getString(SUMO_ATTR_POSITION) + " for access on lane '" + lane->getID() + "' in stop '" + myCurrentStop->getID() + "'.");
554 : }
555 : // add bus stop access
556 2236 : if (!myCurrentStop->addAccess(lane, startPos, endPos, length, exit)) {
557 21 : throw InvalidArgument("Duplicate access on lane '" + lane->getID() + "' for stop '" + myCurrentStop->getID() + "'");
558 : }
559 : }
560 :
561 :
562 : void
563 17407 : NLTriggerBuilder::parseAndBeginParkingArea(MSNet& net, const SUMOSAXAttributes& attrs) {
564 17407 : bool ok = true;
565 : // get the id, throw if not given or empty...
566 17407 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
567 17407 : if (!ok) {
568 0 : throw ProcessError();
569 : }
570 : // get the lane
571 34814 : MSLane* lane = getLane(attrs, "parkingArea", id);
572 : // get the positions
573 17407 : double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
574 17407 : double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
575 17407 : const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
576 17407 : unsigned int capacity = attrs.getOpt<int>(SUMO_ATTR_ROADSIDE_CAPACITY, id.c_str(), ok, 0);
577 17407 : myParkingAreaCapacitySet = attrs.hasAttribute(SUMO_ATTR_ROADSIDE_CAPACITY);
578 17407 : bool onRoad = attrs.getOpt<bool>(SUMO_ATTR_ONROAD, id.c_str(), ok, false);
579 17407 : double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, 0);
580 17407 : double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, 0);
581 17407 : double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, 0);
582 34815 : const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok);
583 34815 : const std::string departPos = attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS, id.c_str(), ok);
584 17407 : bool lefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, id.c_str(), ok, false);
585 17408 : const std::vector<std::string>& acceptedBadges = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_ACCEPTED_BADGES, id.c_str(), ok);
586 17407 : const bool reservable = attrs.getOpt<bool>(SUMO_ATTR_RESERVABLE, id.c_str(), ok, false);
587 :
588 17407 : if (!ok || (myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
589 3 : throw InvalidArgument("Invalid position for parking area '" + id + "'.");
590 : }
591 17406 : const std::vector<std::string>& lines = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LINES, id.c_str(), ok);
592 : // build the parking area
593 17406 : beginParkingArea(net, id, lines, acceptedBadges, lane, frompos, topos, capacity, width, length, angle, name, onRoad, departPos, lefthand, reservable);
594 34813 : }
595 :
596 :
597 :
598 : void
599 655 : NLTriggerBuilder::parseAndAddLotEntry(const SUMOSAXAttributes& attrs) {
600 655 : bool ok = true;
601 : // Check for open parking area
602 655 : if (myParkingArea == nullptr) {
603 0 : throw ProcessError();
604 : }
605 : // get the positions
606 655 : double x = attrs.get<double>(SUMO_ATTR_X, "", ok);
607 655 : if (!ok) {
608 0 : throw InvalidArgument("Invalid x position for lot entry.");
609 : }
610 655 : double y = attrs.get<double>(SUMO_ATTR_Y, "", ok);
611 655 : if (!ok) {
612 0 : throw InvalidArgument("Invalid y position for lot entry.");
613 : }
614 655 : double z = attrs.getOpt<double>(SUMO_ATTR_Z, "", ok, 0.);
615 655 : double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, "", ok, myParkingArea->getWidth());
616 655 : double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, "", ok, myParkingArea->getLength());
617 655 : double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, "", ok, myParkingArea->getAngle());
618 655 : double slope = attrs.getOpt<double>(SUMO_ATTR_SLOPE, "", ok, 0.);
619 : // add the lot entry
620 655 : addLotEntry(x, y, z, width, length, angle, slope);
621 655 : }
622 :
623 :
624 : void
625 923 : NLTriggerBuilder::parseAndBuildCalibrator(MSNet& net, const SUMOSAXAttributes& attrs,
626 : const std::string& base) {
627 923 : bool ok = true;
628 : // get the id, throw if not given or empty...
629 923 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
630 923 : if (!ok) {
631 0 : throw ProcessError();
632 : }
633 : MSLane* lane = nullptr;
634 : MSEdge* edge = nullptr;
635 : MSJunction* node = nullptr;
636 923 : if (attrs.hasAttribute(SUMO_ATTR_NODE)) {
637 14 : if (attrs.hasAttribute(SUMO_ATTR_LANE) || attrs.hasAttribute(SUMO_ATTR_EDGE)) {
638 0 : throw InvalidArgument("The node calibrator '" + id + "' cannot define an edge or lane as well.");
639 : }
640 14 : const std::string nodeID = attrs.get<std::string>(SUMO_ATTR_NODE, id.c_str(), ok);
641 : node = net.getJunctionControl().get(nodeID);
642 14 : if (node == nullptr) {
643 0 : throw InvalidArgument("The node " + nodeID + " to use within the calibrator '" + id + "' is not known.");
644 : }
645 : } else {
646 909 : if (attrs.hasAttribute(SUMO_ATTR_EDGE)) {
647 447 : const std::string edgeID = attrs.get<std::string>(SUMO_ATTR_EDGE, id.c_str(), ok);
648 447 : edge = MSEdge::dictionary(edgeID);
649 447 : if (edge == nullptr) {
650 0 : throw InvalidArgument("The edge " + edgeID + " to use within the calibrator '" + id + "' is not known.");
651 : }
652 447 : if (attrs.hasAttribute(SUMO_ATTR_LANE)) {
653 0 : lane = getLane(attrs, "calibrator", id);
654 0 : if (&lane->getEdge() != edge) {
655 0 : throw InvalidArgument("The edge " + edgeID + " to use within the calibrator '" + id
656 0 : + "' does not match the calibrator lane '" + lane->getID() + ".");
657 : }
658 : }
659 : } else {
660 924 : lane = getLane(attrs, "calibrator", id);
661 : edge = &lane->getEdge();
662 : }
663 : }
664 923 : const double pos = node != nullptr ? 0 : getPosition(attrs, lane, "calibrator", id, edge);
665 923 : SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, DELTA_T); // !!! no error handling
666 923 : if (period > TIME2STEPS(1)) {
667 291 : WRITE_WARNINGF("Reducing period to 1 for calibrator '%'", id);
668 : period = TIME2STEPS(1);
669 : }
670 923 : const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
671 923 : const std::string file = getFileName(attrs, base, true);
672 923 : const std::string outfile = attrs.getOpt<std::string>(SUMO_ATTR_OUTPUT, id.c_str(), ok, "");
673 923 : const std::string routeProbe = attrs.getOpt<std::string>(SUMO_ATTR_ROUTEPROBE, id.c_str(), ok, "");
674 : // differing defaults for backward compatibility, values are dimensionless
675 1758 : const double invalidJamThreshold = attrs.getOpt<double>(SUMO_ATTR_JAM_DIST_THRESHOLD, id.c_str(), ok, MSGlobals::gUseMesoSim ? 0.8 : 0.5);
676 923 : const bool local = attrs.getOpt<bool>(SUMO_ATTR_LOCAL, id.c_str(), ok, false);
677 : MSRouteProbe* probe = nullptr;
678 923 : if (routeProbe != "") {
679 62 : probe = dynamic_cast<MSRouteProbe*>(net.getDetectorControl().getTypedDetectors(SUMO_TAG_ROUTEPROBE).get(routeProbe));
680 31 : if (probe == nullptr) {
681 0 : throw InvalidArgument("The routeProbe '" + routeProbe + "' to use within the calibrator '" + id + "' is not known.");
682 : }
683 : }
684 923 : if (MSGlobals::gUseMesoSim) {
685 88 : if (lane != nullptr && edge->getLanes().size() > 1) {
686 27 : WRITE_WARNING("Meso calibrator '" + id
687 : + "' defined for lane '" + lane->getID()
688 : + "' will collect data for all lanes of edge '" + edge->getID() + "'.");
689 : }
690 88 : METriggeredCalibrator* trigger = buildMECalibrator(id, edge, pos, file, outfile, period, probe, invalidJamThreshold, vTypes);
691 88 : if (file == "") {
692 74 : trigger->registerParent(SUMO_TAG_CALIBRATOR, myHandler);
693 : }
694 : } else {
695 835 : MSCalibrator* trigger = buildCalibrator(id, edge, lane, node, pos, file, outfile, period, probe, invalidJamThreshold, vTypes, local);
696 835 : if (file == "") {
697 800 : trigger->registerParent(SUMO_TAG_CALIBRATOR, myHandler);
698 : }
699 : }
700 923 : }
701 :
702 :
703 : void
704 4011 : NLTriggerBuilder::parseAndBuildRerouter(MSNet& net, const SUMOSAXAttributes& attrs) {
705 4011 : bool ok = true;
706 : // get the id, throw if not given or empty...
707 4011 : std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
708 4011 : if (!ok) {
709 0 : throw ProcessError();
710 : }
711 : if (MSTriggeredRerouter::getInstances().count(id) > 0) {
712 21 : throw InvalidArgument("Could not build rerouter '" + id + "'; probably declared twice.");
713 : }
714 : MSEdgeVector edges;
715 9322 : for (const std::string& edgeID : attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, id.c_str(), ok)) {
716 5318 : MSEdge* edge = MSEdge::dictionary(edgeID);
717 5318 : if (edge == nullptr) {
718 0 : throw InvalidArgument("The edge '" + edgeID + "' to use within rerouter '" + id + "' is not known.");
719 : }
720 5318 : edges.push_back(edge);
721 4004 : }
722 4004 : if (!ok) {
723 0 : throw InvalidArgument("The edge to use within rerouter '" + id + "' is not known.");
724 : }
725 4004 : if (edges.size() == 0) {
726 0 : throw InvalidArgument("No edges found for rerouter '" + id + "'.");
727 : }
728 4004 : const double prob = attrs.getOpt<double>(SUMO_ATTR_PROB, id.c_str(), ok, 1);
729 4004 : const bool off = attrs.getOpt<bool>(SUMO_ATTR_OFF, id.c_str(), ok, false);
730 4004 : const bool optional = attrs.getOpt<bool>(SUMO_ATTR_OPTIONAL, id.c_str(), ok, false);
731 4004 : const SUMOTime timeThreshold = TIME2STEPS(attrs.getOpt<double>(SUMO_ATTR_HALTING_TIME_THRESHOLD, id.c_str(), ok, 0));
732 4004 : const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
733 8008 : const std::string pos = attrs.getOpt<std::string>(SUMO_ATTR_POSITION, id.c_str(), ok, "");
734 4004 : const double radius = attrs.getOpt<double>(SUMO_ATTR_RADIUS, id.c_str(), ok, std::numeric_limits<double>::max());
735 4004 : if (attrs.hasAttribute(SUMO_ATTR_RADIUS) && !attrs.hasAttribute(SUMO_ATTR_POSITION)) {
736 3 : WRITE_WARNINGF(TL("It is strongly advisable to give an explicit position when using radius in the definition of rerouter '%'."), id)
737 : }
738 4004 : Position p = Position::INVALID;
739 4004 : if (pos != "") {
740 1134 : const std::vector<std::string> posSplit = StringTokenizer(pos, ",").getVector();
741 378 : if (posSplit.size() == 1) {
742 2 : double lanePos = StringUtils::toDouble(pos);
743 2 : if (lanePos < 0) {
744 0 : lanePos += edges.front()->getLanes()[0]->getLength();
745 : }
746 2 : p = edges.front()->getLanes()[0]->geometryPositionAtOffset(lanePos);
747 376 : } else if (posSplit.size() == 2) {
748 376 : p = Position(StringUtils::toDouble(posSplit[0]), StringUtils::toDouble(posSplit[1]));
749 0 : } else if (posSplit.size() == 3) {
750 0 : p = Position(StringUtils::toDouble(posSplit[0]), StringUtils::toDouble(posSplit[1]), StringUtils::toDouble(posSplit[2]));
751 : } else {
752 0 : throw InvalidArgument("Invalid position for rerouter '" + id + "'.");
753 : }
754 378 : }
755 4004 : if (!ok) {
756 0 : throw InvalidArgument("Could not parse rerouter '" + id + "'.");
757 : }
758 4004 : MSTriggeredRerouter* trigger = buildRerouter(net, id, edges, prob, off, optional, timeThreshold, vTypes, p, radius);
759 : // read in the trigger description
760 4004 : trigger->registerParent(SUMO_TAG_REROUTER, myHandler);
761 8008 : }
762 :
763 :
764 : // -------------------------
765 :
766 :
767 : MSLaneSpeedTrigger*
768 401 : NLTriggerBuilder::buildLaneSpeedTrigger(MSNet& /*net*/, const std::string& id,
769 : const std::vector<MSLane*>& destLanes,
770 : const std::string& file) {
771 401 : return new MSLaneSpeedTrigger(id, destLanes, file);
772 : }
773 :
774 :
775 : METriggeredCalibrator*
776 88 : NLTriggerBuilder::buildMECalibrator(const std::string& id,
777 : MSEdge* edge,
778 : double pos,
779 : const std::string& file,
780 : const std::string& outfile,
781 : const SUMOTime freq,
782 : MSRouteProbe* probe,
783 : const double invalidJamThreshold,
784 : const std::string& vTypes) {
785 : return new METriggeredCalibrator(id, edge, pos, file, outfile, freq,
786 84 : edge == nullptr ? 0. : MSGlobals::gMesoNet->getSegmentForEdge(*edge, pos)->getLength(),
787 172 : probe, invalidJamThreshold, vTypes);
788 : }
789 :
790 :
791 : MSCalibrator*
792 835 : NLTriggerBuilder::buildCalibrator(const std::string& id,
793 : MSEdge* edge,
794 : MSLane* lane,
795 : MSJunction* node,
796 : double pos,
797 : const std::string& file,
798 : const std::string& outfile,
799 : const SUMOTime freq,
800 : const MSRouteProbe* probe,
801 : const double invalidJamThreshold,
802 : const std::string& vTypes,
803 : const bool local) {
804 : return new MSCalibrator(id, edge, lane, node, pos, file, outfile, freq,
805 : edge == nullptr ? 0. : edge->getLength(),
806 1660 : probe, invalidJamThreshold, vTypes, local, true);
807 : }
808 :
809 :
810 : MSTriggeredRerouter*
811 3276 : NLTriggerBuilder::buildRerouter(MSNet&, const std::string& id,
812 : MSEdgeVector& edges, double prob, bool off, bool optional,
813 : SUMOTime timeThreshold, const std::string& vTypes, const Position& pos, const double radius) {
814 3276 : return new MSTriggeredRerouter(id, edges, prob, off, optional, timeThreshold, vTypes, pos, radius);
815 : }
816 :
817 :
818 : void
819 48690 : NLTriggerBuilder::buildStoppingPlace(MSNet& net, std::string id, std::vector<std::string> lines, MSLane* lane,
820 : double frompos, double topos, const SumoXMLTag element, std::string ptStopName,
821 : int personCapacity, double parkingLength, RGBColor& color, double angle) {
822 97380 : myCurrentStop = new MSStoppingPlace(id, element, lines, *lane, frompos, topos, ptStopName, personCapacity, parkingLength, color, angle);
823 48690 : if (!net.addStoppingPlace(element, myCurrentStop)) {
824 5 : delete myCurrentStop;
825 5 : myCurrentStop = nullptr;
826 15 : throw InvalidArgument("Could not build " + toString(element) + " '" + id + "'; probably declared twice.");
827 : }
828 48685 : }
829 :
830 :
831 : void
832 16533 : NLTriggerBuilder::beginParkingArea(MSNet& net, const std::string& id,
833 : const std::vector<std::string>& lines,
834 : const std::vector<std::string>& badges,
835 : MSLane* lane, double frompos, double topos,
836 : unsigned int capacity,
837 : double width, double length, double angle, const std::string& name,
838 : bool onRoad,
839 : const std::string& departPos,
840 : bool lefthand, bool reservable) {
841 : // Close previous parking area if there are no lots inside
842 16533 : MSParkingArea* stop = new MSParkingArea(id, lines, badges, *lane, frompos, topos, capacity, width, length, angle, name, onRoad, departPos, lefthand, reservable);
843 16533 : if (!net.addStoppingPlace(SUMO_TAG_PARKING_AREA, stop)) {
844 0 : delete stop;
845 0 : throw InvalidArgument("Could not build parking area '" + id + "'; probably declared twice.");
846 : } else {
847 16533 : myParkingArea = stop;
848 : }
849 16533 : }
850 :
851 :
852 : void
853 655 : NLTriggerBuilder::addLotEntry(double x, double y, double z,
854 : double width, double length,
855 : double angle, double slope) {
856 655 : if (myParkingArea != nullptr) {
857 655 : if (!myParkingArea->parkOnRoad()) {
858 655 : myParkingArea->addLotEntry(x, y, z, width, length, angle, slope);
859 655 : myParkingAreaCapacitySet = true;
860 : } else {
861 0 : throw InvalidArgument("Cannot not add lot entry to on-road parking area.");
862 : }
863 : } else {
864 0 : throw InvalidArgument("Could not add lot entry outside a parking area.");
865 : }
866 655 : }
867 :
868 :
869 : void
870 16534 : NLTriggerBuilder::endParkingArea() {
871 16534 : if (myParkingArea != nullptr) {
872 16533 : myParkingArea = nullptr;
873 16533 : myParkingAreaCapacitySet = false;
874 : } else {
875 2 : throw InvalidArgument("Could not end a parking area that is not opened.");
876 : }
877 16533 : }
878 :
879 :
880 : void
881 63634 : NLTriggerBuilder::endStoppingPlace() {
882 63634 : if (myCurrentStop != nullptr) {
883 63625 : myCurrentStop->finishedLoading();
884 63625 : myCurrentStop = nullptr;
885 : } else {
886 18 : throw InvalidArgument("Could not end a stopping place that is not opened.");
887 : }
888 63625 : }
889 :
890 :
891 : void
892 14940 : NLTriggerBuilder::buildChargingStation(MSNet& net, const std::string& id, MSLane* lane, double frompos, double topos,
893 : const std::string& name, double chargingPower, double totalPower, double efficiency,
894 : bool chargeInTransit, SUMOTime chargeDelay, std::string chargeType, SUMOTime waitingTime, MSParkingArea* parkingArea) {
895 14940 : MSChargingStation* chargingStation = (parkingArea == nullptr) ? new MSChargingStation(id, *lane, frompos, topos, name, chargingPower, totalPower, efficiency,
896 14928 : chargeInTransit, chargeDelay, chargeType, waitingTime) : new MSChargingStation(id, parkingArea, name, chargingPower, totalPower, efficiency,
897 12 : chargeInTransit, chargeDelay, chargeType, waitingTime);
898 14940 : if (!net.addStoppingPlace(SUMO_TAG_CHARGING_STATION, chargingStation)) {
899 0 : delete chargingStation;
900 0 : throw InvalidArgument("Could not build charging station '" + id + "'; probably declared twice.");
901 : }
902 14940 : myCurrentStop = chargingStation;
903 14940 : }
904 :
905 :
906 : void
907 50 : NLTriggerBuilder::buildOverheadWireSegment(MSNet& net, const std::string& id, MSLane* lane, double frompos, double topos,
908 : bool voltageSource) {
909 50 : MSOverheadWire* overheadWireSegment = new MSOverheadWire(id, *lane, frompos, topos, voltageSource);
910 50 : if (!net.addStoppingPlace(SUMO_TAG_OVERHEAD_WIRE_SEGMENT, overheadWireSegment)) {
911 0 : delete overheadWireSegment;
912 0 : throw InvalidArgument("Could not build overheadWireSegment '" + id + "'; probably declared twice.");
913 : }
914 50 : }
915 :
916 : void
917 4 : NLTriggerBuilder::buildInnerOverheadWireSegments(MSNet& net, const MSLane* connection, const MSLane* frontConnection, const MSLane* behindConnection) {
918 4 : if (frontConnection == NULL && behindConnection == NULL) {
919 8 : buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
920 0 : } else if (frontConnection != NULL && behindConnection == NULL) {
921 0 : buildOverheadWireSegment(net, "ovrhd_inner_" + frontConnection->getID(), const_cast<MSLane*>(frontConnection), 0, frontConnection->getLength(), false);
922 0 : buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
923 0 : } else if (frontConnection == NULL && behindConnection != NULL) {
924 0 : buildOverheadWireSegment(net, "ovrhd_inner_" + behindConnection->getID(), const_cast<MSLane*>(behindConnection), 0, behindConnection->getLength(), false);
925 0 : buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
926 0 : } else if (frontConnection != NULL && behindConnection != NULL) {
927 0 : buildOverheadWireSegment(net, "ovrhd_inner_" + frontConnection->getID(), const_cast<MSLane*>(frontConnection), 0, frontConnection->getLength(), false);
928 0 : buildOverheadWireSegment(net, "ovrhd_inner_" + behindConnection->getID(), const_cast<MSLane*>(behindConnection), 0, behindConnection->getLength(), false);
929 0 : buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
930 : }
931 4 : }
932 :
933 : void
934 17 : NLTriggerBuilder::buildTractionSubstation(MSNet& net, std::string id, double voltage, double currentLimit) {
935 17 : MSTractionSubstation* myTractionSubstation = new MSTractionSubstation(id, voltage, currentLimit);
936 17 : if (!net.addTractionSubstation(myTractionSubstation)) {
937 0 : delete myTractionSubstation;
938 0 : throw InvalidArgument("Could not build traction substation '" + id + "'; probably declared twice.");
939 : }
940 17 : }
941 :
942 : void
943 10 : NLTriggerBuilder::buildOverheadWireClamp(MSNet& /*net*/, const std::string& /*id*/, MSLane* /*lane_start*/, MSLane* /*lane_end*/) {
944 10 : }
945 :
946 : std::string
947 1426 : NLTriggerBuilder::getFileName(const SUMOSAXAttributes& attrs,
948 : const std::string& base,
949 : const bool allowEmpty) {
950 : // get the file name to read further definitions from
951 1426 : bool ok = true;
952 1426 : std::string file = attrs.getOpt<std::string>(SUMO_ATTR_FILE, nullptr, ok, "");
953 1426 : if (file == "") {
954 1209 : if (allowEmpty) {
955 1209 : return file;
956 : }
957 0 : throw InvalidArgument("No filename given.");
958 : }
959 : // check whether absolute or relative filenames are given
960 217 : if (!FileHelpers::isAbsolute(file)) {
961 217 : return FileHelpers::getConfigurationRelative(base, file);
962 : }
963 0 : return file;
964 : }
965 :
966 :
967 : MSLane*
968 86294 : NLTriggerBuilder::getLane(const SUMOSAXAttributes& attrs,
969 : const std::string& tt,
970 : const std::string& tid) {
971 86294 : bool ok = true;
972 86294 : std::string objectid = attrs.get<std::string>(SUMO_ATTR_LANE, tid.c_str(), ok);
973 86294 : MSLane* lane = MSLane::dictionary(objectid);
974 86294 : if (lane == nullptr) {
975 : // Either a lane that is non-existent/broken, or a lane that is internal and has been ignored.
976 : // We assume that internal lane names start with ':'.
977 0 : if (objectid[0] == ':' && !MSGlobals::gUsingInternalLanes) {
978 : return nullptr;
979 : }
980 : // Throw the exception only in case that the lane really does not exist in the network file
981 : // or it is broken.
982 0 : throw InvalidArgument("The lane " + objectid + " to use within the " + tt + " '" + tid + "' is not known.");
983 : }
984 : return lane;
985 : }
986 :
987 :
988 : MSParkingArea*
989 15241 : NLTriggerBuilder::getParkingArea(const SUMOSAXAttributes& attrs, const std::string& tt, const std::string& tid) {
990 15241 : bool ok = true;
991 15241 : std::string objectID = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, tid.c_str(), ok);
992 15241 : if (!ok || objectID.size() == 0) {
993 : return nullptr;
994 : }
995 17 : MSParkingArea* pa = static_cast<MSParkingArea*>(MSNet::getInstance()->getStoppingPlace(objectID, SUMO_TAG_PARKING_AREA));
996 17 : if (pa == nullptr) {
997 : // Throw the exception only in case that the lane really does not exist in the network file
998 : // or it is broken.
999 0 : throw InvalidArgument("The parkingArea " + objectID + " to use within the " + tt + " '" + tid + "' is not known.");
1000 : }
1001 : return pa;
1002 : }
1003 :
1004 :
1005 : double
1006 909 : NLTriggerBuilder::getPosition(const SUMOSAXAttributes& attrs,
1007 : MSLane* lane,
1008 : const std::string& tt, const std::string& tid,
1009 : MSEdge* edge) {
1010 : assert(lane != nullptr || edge != nullptr);
1011 909 : const double length = lane != nullptr ? lane->getLength() : edge->getLength();
1012 909 : bool ok = true;
1013 909 : double pos = attrs.get<double>(SUMO_ATTR_POSITION, nullptr, ok);
1014 909 : const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, false);
1015 909 : if (!ok) {
1016 0 : throw InvalidArgument("Error on parsing a position information.");
1017 : }
1018 909 : if (pos < 0) {
1019 2 : pos = length + pos;
1020 : }
1021 909 : if (pos > length) {
1022 0 : if (friendlyPos) {
1023 0 : pos = length - (double) 0.1;
1024 : } else {
1025 0 : if (lane != nullptr) {
1026 0 : throw InvalidArgument("The position of " + tt + " '" + tid + "' lies beyond the lane's '" + lane->getID() + "' length.");
1027 : } else {
1028 0 : throw InvalidArgument("The position of " + tt + " '" + tid + "' lies beyond the edge's '" + edge->getID() + "' length.");
1029 : }
1030 : }
1031 : }
1032 909 : return pos;
1033 : }
1034 :
1035 :
1036 : void
1037 17407 : NLTriggerBuilder::updateParkingAreaDefaultCapacity() {
1038 17407 : if (myParkingArea != nullptr && !myParkingAreaCapacitySet) {
1039 12949 : myParkingArea->setRoadsideCapacity(1);
1040 : }
1041 17407 : }
1042 :
1043 :
1044 : MSStoppingPlace*
1045 83524 : NLTriggerBuilder::getCurrentStop() {
1046 83524 : return myParkingArea == nullptr ? myCurrentStop : myParkingArea;
1047 : }
1048 :
1049 :
1050 : /****************************************************************************/
|