Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netload/NLTriggerBuilder.cpp
193871 views
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
NLTriggerBuilder::NLTriggerBuilder()
61
: myHandler(nullptr), myParkingArea(nullptr), myCurrentStop(nullptr) {}
62
63
64
NLTriggerBuilder::~NLTriggerBuilder() {}
65
66
void
67
NLTriggerBuilder::setHandler(NLHandler* handler) {
68
myHandler = handler;
69
}
70
71
72
void
73
NLTriggerBuilder::buildVaporizer(const SUMOSAXAttributes& attrs) {
74
WRITE_WARNING(TL("Vaporizers are deprecated. Use rerouters instead."));
75
bool ok = true;
76
// get the id, throw if not given or empty...
77
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
78
if (!ok) {
79
return;
80
}
81
MSEdge* e = MSEdge::dictionary(id);
82
if (e == nullptr) {
83
WRITE_ERRORF(TL("Unknown edge ('%') referenced in a vaporizer."), id);
84
return;
85
}
86
SUMOTime begin = attrs.getSUMOTimeReporting(SUMO_ATTR_BEGIN, nullptr, ok);
87
SUMOTime end = attrs.getSUMOTimeReporting(SUMO_ATTR_END, nullptr, ok);
88
if (!ok) {
89
return;
90
}
91
if (begin < 0) {
92
WRITE_ERRORF(TL("A vaporization begin time is negative (edge id='%')."), id);
93
return;
94
}
95
if (begin >= end) {
96
WRITE_ERRORF(TL("A vaporization ends before it starts (edge id='%')."), id);
97
return;
98
}
99
if (end >= string2time(OptionsCont::getOptions().getString("begin"))) {
100
Command* cb = new WrappingCommand< MSEdge >(e, &MSEdge::incVaporization);
101
MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(cb, begin);
102
Command* ce = new WrappingCommand< MSEdge >(e, &MSEdge::decVaporization);
103
MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(ce, end);
104
}
105
}
106
107
108
void
109
NLTriggerBuilder::parseAndBuildLaneSpeedTrigger(MSNet& net, const SUMOSAXAttributes& attrs,
110
const std::string& base) {
111
// get the id, throw if not given or empty...
112
bool ok = true;
113
// get the id, throw if not given or empty...
114
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
115
if (!ok) {
116
return;
117
}
118
// get the file name to read further definitions from
119
std::string file = getFileName(attrs, base, true);
120
std::string objectid = attrs.get<std::string>(SUMO_ATTR_LANES, id.c_str(), ok);
121
std::vector<MSLane*> lanes;
122
for (const std::string& laneID : attrs.get<std::vector<std::string> >(SUMO_ATTR_LANES, id.c_str(), ok)) {
123
MSLane* lane = MSLane::dictionary(laneID);
124
if (lane == nullptr) {
125
throw InvalidArgument("The lane '" + laneID + "' to use within MSLaneSpeedTrigger '" + id + "' is not known.");
126
}
127
lanes.push_back(lane);
128
}
129
if (!ok) {
130
throw InvalidArgument("The lanes to use within MSLaneSpeedTrigger '" + id + "' are not known.");
131
}
132
if (lanes.size() == 0) {
133
throw InvalidArgument("No lane defined for MSLaneSpeedTrigger '" + id + "'.");
134
}
135
try {
136
MSLaneSpeedTrigger* trigger = buildLaneSpeedTrigger(net, id, lanes, file);
137
if (file == "") {
138
trigger->registerParent(SUMO_TAG_VSS, myHandler);
139
}
140
} catch (ProcessError& e) {
141
throw InvalidArgument(e.what());
142
}
143
}
144
145
146
void
147
NLTriggerBuilder::parseAndBuildChargingStation(MSNet& net, const SUMOSAXAttributes& attrs) {
148
bool ok = true;
149
150
// get the id, throw if not given or empty...
151
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
152
if (!ok) {
153
throw ProcessError();
154
}
155
156
MSLane* const lane = getLane(attrs, "chargingStation", id);
157
double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
158
double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
159
const double chargingPower = attrs.getOpt<double>(SUMO_ATTR_CHARGINGPOWER, id.c_str(), ok, 22000);
160
const double totalPower = attrs.getOpt<double>(SUMO_ATTR_TOTALPOWER, id.c_str(), ok, -1);
161
const double efficiency = attrs.getOpt<double>(SUMO_ATTR_EFFICIENCY, id.c_str(), ok, 0.95);
162
const bool chargeInTransit = attrs.getOpt<bool>(SUMO_ATTR_CHARGEINTRANSIT, id.c_str(), ok, 0);
163
const SUMOTime chargeDelay = attrs.getOptSUMOTimeReporting(SUMO_ATTR_CHARGEDELAY, id.c_str(), ok, 0);
164
const std::string chargeType = attrs.getOpt<std::string>(SUMO_ATTR_CHARGETYPE, id.c_str(), ok, "normal");
165
const SUMOTime waitingTime = attrs.getOptSUMOTimeReporting(SUMO_ATTR_WAITINGTIME, id.c_str(), ok, 900);
166
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
167
const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
168
MSParkingArea* parkingArea = getParkingArea(attrs, "parkingArea", id);
169
170
// check charge type
171
if ((chargeType != "normal") && (chargeType != "battery-exchange") && (chargeType != "fuel")) {
172
throw InvalidArgument("The chargeType to use within MSLaneSpeedTrigger '" + id + "' is invalid.");
173
}
174
175
if (!ok || (myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
176
throw InvalidArgument("Invalid position for charging station '" + id + "'.");
177
}
178
179
buildChargingStation(net, id, lane, frompos, topos, name, chargingPower, totalPower, efficiency, chargeInTransit, chargeDelay, chargeType, waitingTime, parkingArea);
180
}
181
182
183
void
184
NLTriggerBuilder::parseAndBuildOverheadWireSegment(MSNet& net, const SUMOSAXAttributes& attrs) {
185
bool ok = true;
186
187
// get the id, throw if not given or empty...
188
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
189
if (!ok) {
190
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
MSLane* const lane = getLane(attrs, "overheadWireSegment", id);
198
if (lane == nullptr) {
199
WRITE_MESSAGEF(TL("The overheadWireSegment '%' was not created as it is attached to internal lane. It will be build automatically."), id);
200
return;
201
}
202
203
if (lane->isInternal()) {
204
WRITE_MESSAGEF(TL("The overheadWireSegment '%' not built as it is attached to internal lane. It will be build automatically."), id);
205
return;
206
}
207
208
double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
209
double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
210
const bool voltageSource = attrs.getOpt<bool>(SUMO_ATTR_VOLTAGESOURCE, id.c_str(), ok, false);
211
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
212
213
if (!ok || myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID) {
214
frompos = 0;
215
topos = lane->getLength();
216
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
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
NLTriggerBuilder::parseAndBuildOverheadWireSection(MSNet& net, const SUMOSAXAttributes& attrs) {
231
bool ok = true;
232
std::string substationId = attrs.get<std::string>(SUMO_ATTR_SUBSTATIONID, 0, ok);
233
if (!ok) {
234
throw ProcessError();
235
}
236
237
MSTractionSubstation* substation = MSNet::getInstance()->findTractionSubstation(substationId);
238
if (substation == nullptr) {
239
throw InvalidArgument("Traction substation '" + substationId + "' refereced by an OverheadWire Section is not known.");
240
} else if (substation->isAnySectionPreviouslyDefined()) {
241
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
std::string segmentStrings = attrs.get<std::string>(SUMO_ATTR_OVERHEAD_WIRE_SEGMENTS, substationId.c_str(), ok);
246
if (!ok) {
247
throw InvalidArgument("Segments referenced by Traction substation '" + substationId + "' are not declared .");
248
}
249
250
// process forbidden internal lanes
251
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
for (const std::string& laneID : forbiddenInnerLanesIDs) {
254
MSLane* lane = MSLane::dictionary(laneID);
255
if (lane != nullptr) {
256
substation->addForbiddenLane(lane);
257
} else {
258
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
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
for (const std::string& segmentID : segmentIDs) {
275
const MSLane* connection = nullptr;
276
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
if (ovrhdSegment == nullptr) {
281
throw InvalidArgument("The OverheadWireSegment with id='" + segmentID + "' referenced by OverheadWireSegment for substation '" + substationId + "' was not defined.");
282
}
283
284
MSTractionSubstation* ts = ovrhdSegment->getTractionSubstation();
285
if (!(ts == substation || ts == nullptr)) {
286
std::string tsName = ts->getID();
287
throw InvalidArgument("The OverheadWireSegment '" + segmentID + "' referenced by OverheadWireSegment for substation '" + substationId + "' is already assigned to substation '" + tsName + "'.");
288
}
289
ovrhdSegment->setTractionSubstation(substation);
290
291
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
const std::vector<std::pair<const MSLane*, const MSEdge*> > outgoingLanesAndEdges = lane->getOutgoingViaLanes();
296
std::vector<const MSLane*> neigboringInnerLanes;
297
neigboringInnerLanes.reserve(outgoingLanesAndEdges.size());
298
for (size_t it = 0; it < outgoingLanesAndEdges.size(); ++it) {
299
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
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
neigboringOvrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
307
if (neigboringOvrhdSegmentID != "") {
308
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
if (neigboringOvrhdSegmentTractionSubstation == substation && !(*it)->isInternal()) {
316
connection = lane->getInternalFollowingLane(*it);
317
if (connection != nullptr) {
318
//is connection forbidden?
319
if (!(substation->isForbidden(connection) || substation->isForbidden(lane->getInternalFollowingLane(connection)) || substation->isForbidden(connection->getInternalFollowingLane(*it)))) {
320
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
neigboringInnerLanes = lane->getNormalIncomingLanes();
329
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
neigboringOvrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, (*it)->getLength() - NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
332
if (neigboringOvrhdSegmentID != "") {
333
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
if (neigboringOvrhdSegmentTractionSubstation == substation && !(*it)->isInternal()) {
341
connection = (*it)->getInternalFollowingLane(lane);
342
if (connection != nullptr) {
343
//is connection forbidden?
344
if (!(substation->isForbidden(connection) || substation->isForbidden((*it)->getInternalFollowingLane(connection)) || substation->isForbidden(connection->getInternalFollowingLane(lane)))) {
345
buildInnerOverheadWireSegments(net, connection, (*it)->getInternalFollowingLane(connection), connection->getInternalFollowingLane(lane));
346
}
347
}
348
}
349
}
350
}
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
for (const std::string& segmentID : segmentIDs) {
357
MSOverheadWire* ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(segmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
358
ovrhdSegment->setTractionSubstation(nullptr);
359
}
360
361
for (const std::string& segmentID : segmentIDs) {
362
if (segmentID == "") {
363
continue;
364
}
365
MSOverheadWire* ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(segmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
366
substation->addOverheadWireSegmentToCircuit(ovrhdSegment);
367
segments.push_back(ovrhdSegment);
368
}
369
370
// adding overhead wire clamp
371
std::string clampsString = attrs.getOpt<std::string>(SUMO_ATTR_OVERHEAD_WIRE_CLAMPS, nullptr, ok, "");
372
if (clampsString != "" && MSGlobals::gOverheadWireSolver) {
373
#ifdef HAVE_EIGEN
374
const std::vector<std::string>& clampIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_OVERHEAD_WIRE_CLAMPS, nullptr, ok);
375
for (const std::string& clampID : clampIDs) {
376
MSTractionSubstation::OverheadWireClamp* clamp = substation->findClamp(clampID);
377
if (clamp != nullptr) {
378
if (clamp->start->getTractionSubstation() == substation && clamp->end->getTractionSubstation() == substation) {
379
substation->addOverheadWireClampToCircuit(clamp->id, clamp->start, clamp->end);
380
buildOverheadWireClamp(net, clamp->id, const_cast<MSLane*>(&clamp->start->getLane()), const_cast<MSLane*>(&clamp->end->getLane()));
381
clamp->usage = true;
382
} else {
383
if (clamp->start->getTractionSubstation() != substation) {
384
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
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
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
}
397
398
if (segments.size() == 0) {
399
throw InvalidArgument("No segments found for overHeadWireSection '" + substationId + "'.");
400
} else if (MSGlobals::gOverheadWireSolver) {
401
#ifdef HAVE_EIGEN
402
// check that the electric circuit makes sense
403
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
}
409
410
void
411
NLTriggerBuilder::parseAndBuildTractionSubstation(MSNet& net, const SUMOSAXAttributes& attrs) {
412
bool ok = true;
413
414
// get the id, throw if not given or empty...
415
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
416
if (!ok) {
417
throw ProcessError();
418
}
419
420
// RICE_TODO Limits are fixed, change them to some predefined constants ...
421
const double voltage = attrs.getOpt<double>(SUMO_ATTR_VOLTAGE, id.c_str(), ok, 600);
422
const double currentLimit = attrs.getOpt<double>(SUMO_ATTR_CURRENTLIMIT, id.c_str(), ok, 400);
423
buildTractionSubstation(net, id, voltage, currentLimit);
424
}
425
426
void
427
NLTriggerBuilder::parseAndBuildOverheadWireClamp(MSNet& /*net*/, const SUMOSAXAttributes& attrs) {
428
if (MSGlobals::gOverheadWireSolver) {
429
#ifdef HAVE_EIGEN
430
bool ok = true;
431
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
432
if (!ok) {
433
throw ProcessError();
434
}
435
436
std::string substationId = attrs.get<std::string>(SUMO_ATTR_SUBSTATIONID, 0, ok);
437
if (!ok) {
438
throw ProcessError();
439
}
440
MSTractionSubstation* substation = MSNet::getInstance()->findTractionSubstation(substationId);
441
if (substation == nullptr) {
442
throw InvalidArgument("Traction substation '" + substationId + "' using within an overheadWireClamp '" + id + "' is not known.");
443
}
444
445
std::string overhead_fromItsStart = attrs.get<std::string>(SUMO_ATTR_OVERHEAD_WIRE_CLAMP_START, 0, ok);
446
if (!ok) {
447
throw ProcessError();
448
}
449
MSOverheadWire* ovrhdSegment_fromItsStart = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(overhead_fromItsStart, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
450
if (ovrhdSegment_fromItsStart == nullptr) {
451
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
std::string overhead_fromItsEnd = attrs.get<std::string>(SUMO_ATTR_OVERHEAD_WIRE_CLAMP_END, 0, ok);
458
if (!ok) {
459
throw ProcessError();
460
}
461
MSOverheadWire* ovrhdSegment_fromItsEnd = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(overhead_fromItsEnd, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
462
if (ovrhdSegment_fromItsEnd == nullptr) {
463
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
if (substation->findClamp(id) == nullptr) {
471
substation->addClamp(id, ovrhdSegment_fromItsStart, ovrhdSegment_fromItsEnd);
472
} else {
473
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
WRITE_WARNING(TL("Ignoring overhead wire clamps, they make no sense when overhead wire circuit solver is off."));
481
}
482
}
483
484
485
void
486
NLTriggerBuilder::parseAndBuildStoppingPlace(MSNet& net, const SUMOSAXAttributes& attrs, const SumoXMLTag element) {
487
bool ok = true;
488
// get the id, throw if not given or empty...
489
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
490
if (!ok) {
491
throw ProcessError();
492
}
493
494
//get the name, leave blank if not given
495
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
RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok, RGBColor::INVISIBLE);
500
501
MSLane* lane = getLane(attrs, toString(element), id);
502
// get the positions
503
double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0.);
504
double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
505
double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, 90);
506
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
507
if (!ok || (myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
508
throw InvalidArgument("Invalid position for " + toString(element) + " '" + id + "'.");
509
}
510
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
if (element != SUMO_TAG_CONTAINER_STOP) {
514
defaultCapacity = MAX2(MSStoppingPlace::getDefaultTransportablesAbreast(topos - frompos, element) * 3, 6);
515
capacityAttr = SUMO_ATTR_PERSON_CAPACITY;
516
} else {
517
defaultCapacity = MSStoppingPlace::getDefaultTransportablesAbreast(topos - frompos, element);
518
capacityAttr = SUMO_ATTR_CONTAINER_CAPACITY;
519
}
520
const int transportableCapacity = attrs.getOpt<int>(capacityAttr, id.c_str(), ok, defaultCapacity);
521
const double parkingLength = attrs.getOpt<double>(SUMO_ATTR_PARKING_LENGTH, id.c_str(), ok, 0);
522
// build the bus stop
523
buildStoppingPlace(net, id, lines, lane, frompos, topos, element, ptStopName, transportableCapacity, parkingLength, color, angle);
524
}
525
526
527
void
528
NLTriggerBuilder::addAccess(MSNet& /* net */, const SUMOSAXAttributes& attrs) {
529
if (myCurrentStop == nullptr) {
530
throw InvalidArgument("Could not add access outside a stopping place.");
531
}
532
// get the lane
533
MSLane* lane = getLane(attrs, "access", myCurrentStop->getID());
534
if (!lane->allowsVehicleClass(SVC_PEDESTRIAN)) {
535
WRITE_WARNINGF(TL("Ignoring invalid access from non-pedestrian lane '%' in busStop '%'."), lane->getID(), myCurrentStop->getID());
536
return;
537
}
538
// get the positions
539
bool ok = true;
540
const std::string accessPos = attrs.getOpt<std::string>(SUMO_ATTR_POSITION, "access", ok);
541
const bool random = accessPos == "random";
542
MSStoppingPlace::AccessExit exit = MSStoppingPlace::AccessExit::PLATFORM;
543
if (accessPos == "doors") {
544
exit = MSStoppingPlace::AccessExit::DOORS;
545
} else if (accessPos == "carriage") {
546
exit = MSStoppingPlace::AccessExit::CARRIAGE;
547
}
548
double startPos = random || exit != MSStoppingPlace::AccessExit::PLATFORM ? 0. : attrs.getOpt<double>(SUMO_ATTR_POSITION, "access", ok, 0);
549
double endPos = random || exit != MSStoppingPlace::AccessExit::PLATFORM ? lane->getLength() : startPos;
550
const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, "access", ok, -1);
551
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, "access", ok, false);
552
if (!ok || (myHandler->checkStopPos(startPos, endPos, lane->getLength(), 0, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
553
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
if (!myCurrentStop->addAccess(lane, startPos, endPos, length, exit)) {
557
throw InvalidArgument("Duplicate access on lane '" + lane->getID() + "' for stop '" + myCurrentStop->getID() + "'");
558
}
559
}
560
561
562
void
563
NLTriggerBuilder::parseAndBeginParkingArea(MSNet& net, const SUMOSAXAttributes& attrs) {
564
bool ok = true;
565
// get the id, throw if not given or empty...
566
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
567
if (!ok) {
568
throw ProcessError();
569
}
570
// get the lane
571
MSLane* lane = getLane(attrs, "parkingArea", id);
572
// get the positions
573
double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
574
double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
575
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
576
unsigned int capacity = attrs.getOpt<int>(SUMO_ATTR_ROADSIDE_CAPACITY, id.c_str(), ok, 0);
577
myParkingAreaCapacitySet = attrs.hasAttribute(SUMO_ATTR_ROADSIDE_CAPACITY);
578
bool onRoad = attrs.getOpt<bool>(SUMO_ATTR_ONROAD, id.c_str(), ok, false);
579
double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, 0);
580
double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, 0);
581
double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, 0);
582
const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok);
583
const std::string departPos = attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS, id.c_str(), ok);
584
bool lefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, id.c_str(), ok, false);
585
const std::vector<std::string>& acceptedBadges = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_ACCEPTED_BADGES, id.c_str(), ok);
586
587
if (!ok || (myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
588
throw InvalidArgument("Invalid position for parking area '" + id + "'.");
589
}
590
const std::vector<std::string>& lines = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LINES, id.c_str(), ok);
591
// build the parking area
592
beginParkingArea(net, id, lines, acceptedBadges, lane, frompos, topos, capacity, width, length, angle, name, onRoad, departPos, lefthand);
593
}
594
595
596
597
void
598
NLTriggerBuilder::parseAndAddLotEntry(const SUMOSAXAttributes& attrs) {
599
bool ok = true;
600
// Check for open parking area
601
if (myParkingArea == nullptr) {
602
throw ProcessError();
603
}
604
// get the positions
605
double x = attrs.get<double>(SUMO_ATTR_X, "", ok);
606
if (!ok) {
607
throw InvalidArgument("Invalid x position for lot entry.");
608
}
609
double y = attrs.get<double>(SUMO_ATTR_Y, "", ok);
610
if (!ok) {
611
throw InvalidArgument("Invalid y position for lot entry.");
612
}
613
double z = attrs.getOpt<double>(SUMO_ATTR_Z, "", ok, 0.);
614
double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, "", ok, myParkingArea->getWidth());
615
double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, "", ok, myParkingArea->getLength());
616
double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, "", ok, myParkingArea->getAngle());
617
double slope = attrs.getOpt<double>(SUMO_ATTR_SLOPE, "", ok, 0.);
618
// add the lot entry
619
addLotEntry(x, y, z, width, length, angle, slope);
620
}
621
622
623
void
624
NLTriggerBuilder::parseAndBuildCalibrator(MSNet& net, const SUMOSAXAttributes& attrs,
625
const std::string& base) {
626
bool ok = true;
627
// get the id, throw if not given or empty...
628
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
629
if (!ok) {
630
throw ProcessError();
631
}
632
MSLane* lane = nullptr;
633
MSEdge* edge = nullptr;
634
MSJunction* node = nullptr;
635
if (attrs.hasAttribute(SUMO_ATTR_NODE)) {
636
if (attrs.hasAttribute(SUMO_ATTR_LANE) || attrs.hasAttribute(SUMO_ATTR_EDGE)) {
637
throw InvalidArgument("The node calibrator '" + id + "' cannot define an edge or lane as well.");
638
}
639
const std::string nodeID = attrs.get<std::string>(SUMO_ATTR_NODE, id.c_str(), ok);
640
node = net.getJunctionControl().get(nodeID);
641
if (node == nullptr) {
642
throw InvalidArgument("The node " + nodeID + " to use within the calibrator '" + id + "' is not known.");
643
}
644
} else {
645
if (attrs.hasAttribute(SUMO_ATTR_EDGE)) {
646
const std::string edgeID = attrs.get<std::string>(SUMO_ATTR_EDGE, id.c_str(), ok);
647
edge = MSEdge::dictionary(edgeID);
648
if (edge == nullptr) {
649
throw InvalidArgument("The edge " + edgeID + " to use within the calibrator '" + id + "' is not known.");
650
}
651
if (attrs.hasAttribute(SUMO_ATTR_LANE)) {
652
lane = getLane(attrs, "calibrator", id);
653
if (&lane->getEdge() != edge) {
654
throw InvalidArgument("The edge " + edgeID + " to use within the calibrator '" + id
655
+ "' does not match the calibrator lane '" + lane->getID() + ".");
656
}
657
}
658
} else {
659
lane = getLane(attrs, "calibrator", id);
660
edge = &lane->getEdge();
661
}
662
}
663
const double pos = node != nullptr ? 0 : getPosition(attrs, lane, "calibrator", id, edge);
664
SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, DELTA_T); // !!! no error handling
665
if (period > TIME2STEPS(1)) {
666
WRITE_WARNINGF("Reducing period to 1 for calibrator '%'", id);
667
period = TIME2STEPS(1);
668
}
669
const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
670
const std::string file = getFileName(attrs, base, true);
671
const std::string outfile = attrs.getOpt<std::string>(SUMO_ATTR_OUTPUT, id.c_str(), ok, "");
672
const std::string routeProbe = attrs.getOpt<std::string>(SUMO_ATTR_ROUTEPROBE, id.c_str(), ok, "");
673
// differing defaults for backward compatibility, values are dimensionless
674
const double invalidJamThreshold = attrs.getOpt<double>(SUMO_ATTR_JAM_DIST_THRESHOLD, id.c_str(), ok, MSGlobals::gUseMesoSim ? 0.8 : 0.5);
675
const bool local = attrs.getOpt<bool>(SUMO_ATTR_LOCAL, id.c_str(), ok, false);
676
MSRouteProbe* probe = nullptr;
677
if (routeProbe != "") {
678
probe = dynamic_cast<MSRouteProbe*>(net.getDetectorControl().getTypedDetectors(SUMO_TAG_ROUTEPROBE).get(routeProbe));
679
if (probe == nullptr) {
680
throw InvalidArgument("The routeProbe '" + routeProbe + "' to use within the calibrator '" + id + "' is not known.");
681
}
682
}
683
if (MSGlobals::gUseMesoSim) {
684
if (lane != nullptr && edge->getLanes().size() > 1) {
685
WRITE_WARNING("Meso calibrator '" + id
686
+ "' defined for lane '" + lane->getID()
687
+ "' will collect data for all lanes of edge '" + edge->getID() + "'.");
688
}
689
METriggeredCalibrator* trigger = buildMECalibrator(id, edge, pos, file, outfile, period, probe, invalidJamThreshold, vTypes);
690
if (file == "") {
691
trigger->registerParent(SUMO_TAG_CALIBRATOR, myHandler);
692
}
693
} else {
694
MSCalibrator* trigger = buildCalibrator(id, edge, lane, node, pos, file, outfile, period, probe, invalidJamThreshold, vTypes, local);
695
if (file == "") {
696
trigger->registerParent(SUMO_TAG_CALIBRATOR, myHandler);
697
}
698
}
699
}
700
701
702
void
703
NLTriggerBuilder::parseAndBuildRerouter(MSNet& net, const SUMOSAXAttributes& attrs) {
704
bool ok = true;
705
// get the id, throw if not given or empty...
706
std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
707
if (!ok) {
708
throw ProcessError();
709
}
710
if (MSTriggeredRerouter::getInstances().count(id) > 0) {
711
throw InvalidArgument("Could not build rerouter '" + id + "'; probably declared twice.");
712
}
713
MSEdgeVector edges;
714
for (const std::string& edgeID : attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, id.c_str(), ok)) {
715
MSEdge* edge = MSEdge::dictionary(edgeID);
716
if (edge == nullptr) {
717
throw InvalidArgument("The edge '" + edgeID + "' to use within rerouter '" + id + "' is not known.");
718
}
719
edges.push_back(edge);
720
}
721
if (!ok) {
722
throw InvalidArgument("The edge to use within rerouter '" + id + "' is not known.");
723
}
724
if (edges.size() == 0) {
725
throw InvalidArgument("No edges found for rerouter '" + id + "'.");
726
}
727
const double prob = attrs.getOpt<double>(SUMO_ATTR_PROB, id.c_str(), ok, 1);
728
const bool off = attrs.getOpt<bool>(SUMO_ATTR_OFF, id.c_str(), ok, false);
729
const bool optional = attrs.getOpt<bool>(SUMO_ATTR_OPTIONAL, id.c_str(), ok, false);
730
const SUMOTime timeThreshold = TIME2STEPS(attrs.getOpt<double>(SUMO_ATTR_HALTING_TIME_THRESHOLD, id.c_str(), ok, 0));
731
const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
732
const std::string pos = attrs.getOpt<std::string>(SUMO_ATTR_POSITION, id.c_str(), ok, "");
733
const double radius = attrs.getOpt<double>(SUMO_ATTR_RADIUS, id.c_str(), ok, std::numeric_limits<double>::max());
734
if (attrs.hasAttribute(SUMO_ATTR_RADIUS) && !attrs.hasAttribute(SUMO_ATTR_POSITION)) {
735
WRITE_WARNINGF(TL("It is strongly advisable to give an explicit position when using radius in the definition of rerouter '%'."), id)
736
}
737
Position p = Position::INVALID;
738
if (pos != "") {
739
const std::vector<std::string> posSplit = StringTokenizer(pos, ",").getVector();
740
if (posSplit.size() == 1) {
741
double lanePos = StringUtils::toDouble(pos);
742
if (lanePos < 0) {
743
lanePos += edges.front()->getLanes()[0]->getLength();
744
}
745
p = edges.front()->getLanes()[0]->geometryPositionAtOffset(lanePos);
746
} else if (posSplit.size() == 2) {
747
p = Position(StringUtils::toDouble(posSplit[0]), StringUtils::toDouble(posSplit[1]));
748
} else if (posSplit.size() == 3) {
749
p = Position(StringUtils::toDouble(posSplit[0]), StringUtils::toDouble(posSplit[1]), StringUtils::toDouble(posSplit[2]));
750
} else {
751
throw InvalidArgument("Invalid position for rerouter '" + id + "'.");
752
}
753
}
754
if (!ok) {
755
throw InvalidArgument("Could not parse rerouter '" + id + "'.");
756
}
757
MSTriggeredRerouter* trigger = buildRerouter(net, id, edges, prob, off, optional, timeThreshold, vTypes, p, radius);
758
// read in the trigger description
759
trigger->registerParent(SUMO_TAG_REROUTER, myHandler);
760
}
761
762
763
// -------------------------
764
765
766
MSLaneSpeedTrigger*
767
NLTriggerBuilder::buildLaneSpeedTrigger(MSNet& /*net*/, const std::string& id,
768
const std::vector<MSLane*>& destLanes,
769
const std::string& file) {
770
return new MSLaneSpeedTrigger(id, destLanes, file);
771
}
772
773
774
METriggeredCalibrator*
775
NLTriggerBuilder::buildMECalibrator(const std::string& id,
776
MSEdge* edge,
777
double pos,
778
const std::string& file,
779
const std::string& outfile,
780
const SUMOTime freq,
781
MSRouteProbe* probe,
782
const double invalidJamThreshold,
783
const std::string& vTypes) {
784
return new METriggeredCalibrator(id, edge, pos, file, outfile, freq,
785
edge == nullptr ? 0. : MSGlobals::gMesoNet->getSegmentForEdge(*edge, pos)->getLength(),
786
probe, invalidJamThreshold, vTypes);
787
}
788
789
790
MSCalibrator*
791
NLTriggerBuilder::buildCalibrator(const std::string& id,
792
MSEdge* edge,
793
MSLane* lane,
794
MSJunction* node,
795
double pos,
796
const std::string& file,
797
const std::string& outfile,
798
const SUMOTime freq,
799
const MSRouteProbe* probe,
800
const double invalidJamThreshold,
801
const std::string& vTypes,
802
const bool local) {
803
return new MSCalibrator(id, edge, lane, node, pos, file, outfile, freq,
804
edge == nullptr ? 0. : edge->getLength(),
805
probe, invalidJamThreshold, vTypes, local, true);
806
}
807
808
809
MSTriggeredRerouter*
810
NLTriggerBuilder::buildRerouter(MSNet&, const std::string& id,
811
MSEdgeVector& edges, double prob, bool off, bool optional,
812
SUMOTime timeThreshold, const std::string& vTypes, const Position& pos, const double radius) {
813
return new MSTriggeredRerouter(id, edges, prob, off, optional, timeThreshold, vTypes, pos, radius);
814
}
815
816
817
void
818
NLTriggerBuilder::buildStoppingPlace(MSNet& net, std::string id, std::vector<std::string> lines, MSLane* lane,
819
double frompos, double topos, const SumoXMLTag element, std::string ptStopName,
820
int personCapacity, double parkingLength, RGBColor& color, double angle) {
821
myCurrentStop = new MSStoppingPlace(id, element, lines, *lane, frompos, topos, ptStopName, personCapacity, parkingLength, color, angle);
822
if (!net.addStoppingPlace(element, myCurrentStop)) {
823
delete myCurrentStop;
824
myCurrentStop = nullptr;
825
throw InvalidArgument("Could not build " + toString(element) + " '" + id + "'; probably declared twice.");
826
}
827
}
828
829
830
void
831
NLTriggerBuilder::beginParkingArea(MSNet& net, const std::string& id,
832
const std::vector<std::string>& lines,
833
const std::vector<std::string>& badges,
834
MSLane* lane, double frompos, double topos,
835
unsigned int capacity,
836
double width, double length, double angle, const std::string& name,
837
bool onRoad,
838
const std::string& departPos,
839
bool lefthand) {
840
// Close previous parking area if there are no lots inside
841
MSParkingArea* stop = new MSParkingArea(id, lines, badges, *lane, frompos, topos, capacity, width, length, angle, name, onRoad, departPos, lefthand);
842
if (!net.addStoppingPlace(SUMO_TAG_PARKING_AREA, stop)) {
843
delete stop;
844
throw InvalidArgument("Could not build parking area '" + id + "'; probably declared twice.");
845
} else {
846
myParkingArea = stop;
847
}
848
}
849
850
851
void
852
NLTriggerBuilder::addLotEntry(double x, double y, double z,
853
double width, double length,
854
double angle, double slope) {
855
if (myParkingArea != nullptr) {
856
if (!myParkingArea->parkOnRoad()) {
857
myParkingArea->addLotEntry(x, y, z, width, length, angle, slope);
858
myParkingAreaCapacitySet = true;
859
} else {
860
throw InvalidArgument("Cannot not add lot entry to on-road parking area.");
861
}
862
} else {
863
throw InvalidArgument("Could not add lot entry outside a parking area.");
864
}
865
}
866
867
868
void
869
NLTriggerBuilder::endParkingArea() {
870
if (myParkingArea != nullptr) {
871
myParkingArea = nullptr;
872
myParkingAreaCapacitySet = false;
873
} else {
874
throw InvalidArgument("Could not end a parking area that is not opened.");
875
}
876
}
877
878
879
void
880
NLTriggerBuilder::endStoppingPlace() {
881
if (myCurrentStop != nullptr) {
882
myCurrentStop->finishedLoading();
883
myCurrentStop = nullptr;
884
} else {
885
throw InvalidArgument("Could not end a stopping place that is not opened.");
886
}
887
}
888
889
890
void
891
NLTriggerBuilder::buildChargingStation(MSNet& net, const std::string& id, MSLane* lane, double frompos, double topos,
892
const std::string& name, double chargingPower, double totalPower, double efficiency,
893
bool chargeInTransit, SUMOTime chargeDelay, std::string chargeType, SUMOTime waitingTime, MSParkingArea* parkingArea) {
894
MSChargingStation* chargingStation = (parkingArea == nullptr) ? new MSChargingStation(id, *lane, frompos, topos, name, chargingPower, totalPower, efficiency,
895
chargeInTransit, chargeDelay, chargeType, waitingTime) : new MSChargingStation(id, parkingArea, name, chargingPower, totalPower, efficiency,
896
chargeInTransit, chargeDelay, chargeType, waitingTime);
897
if (!net.addStoppingPlace(SUMO_TAG_CHARGING_STATION, chargingStation)) {
898
delete chargingStation;
899
throw InvalidArgument("Could not build charging station '" + id + "'; probably declared twice.");
900
}
901
myCurrentStop = chargingStation;
902
}
903
904
905
void
906
NLTriggerBuilder::buildOverheadWireSegment(MSNet& net, const std::string& id, MSLane* lane, double frompos, double topos,
907
bool voltageSource) {
908
MSOverheadWire* overheadWireSegment = new MSOverheadWire(id, *lane, frompos, topos, voltageSource);
909
if (!net.addStoppingPlace(SUMO_TAG_OVERHEAD_WIRE_SEGMENT, overheadWireSegment)) {
910
delete overheadWireSegment;
911
throw InvalidArgument("Could not build overheadWireSegment '" + id + "'; probably declared twice.");
912
}
913
}
914
915
void
916
NLTriggerBuilder::buildInnerOverheadWireSegments(MSNet& net, const MSLane* connection, const MSLane* frontConnection, const MSLane* behindConnection) {
917
if (frontConnection == NULL && behindConnection == NULL) {
918
buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
919
} else if (frontConnection != NULL && behindConnection == NULL) {
920
buildOverheadWireSegment(net, "ovrhd_inner_" + frontConnection->getID(), const_cast<MSLane*>(frontConnection), 0, frontConnection->getLength(), false);
921
buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
922
} else if (frontConnection == NULL && behindConnection != NULL) {
923
buildOverheadWireSegment(net, "ovrhd_inner_" + behindConnection->getID(), const_cast<MSLane*>(behindConnection), 0, behindConnection->getLength(), false);
924
buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
925
} else if (frontConnection != NULL && behindConnection != NULL) {
926
buildOverheadWireSegment(net, "ovrhd_inner_" + frontConnection->getID(), const_cast<MSLane*>(frontConnection), 0, frontConnection->getLength(), false);
927
buildOverheadWireSegment(net, "ovrhd_inner_" + behindConnection->getID(), const_cast<MSLane*>(behindConnection), 0, behindConnection->getLength(), false);
928
buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
929
}
930
}
931
932
void
933
NLTriggerBuilder::buildTractionSubstation(MSNet& net, std::string id, double voltage, double currentLimit) {
934
MSTractionSubstation* myTractionSubstation = new MSTractionSubstation(id, voltage, currentLimit);
935
if (!net.addTractionSubstation(myTractionSubstation)) {
936
delete myTractionSubstation;
937
throw InvalidArgument("Could not build traction substation '" + id + "'; probably declared twice.");
938
}
939
}
940
941
void
942
NLTriggerBuilder::buildOverheadWireClamp(MSNet& /*net*/, const std::string& /*id*/, MSLane* /*lane_start*/, MSLane* /*lane_end*/) {
943
}
944
945
std::string
946
NLTriggerBuilder::getFileName(const SUMOSAXAttributes& attrs,
947
const std::string& base,
948
const bool allowEmpty) {
949
// get the file name to read further definitions from
950
bool ok = true;
951
std::string file = attrs.getOpt<std::string>(SUMO_ATTR_FILE, nullptr, ok, "");
952
if (file == "") {
953
if (allowEmpty) {
954
return file;
955
}
956
throw InvalidArgument("No filename given.");
957
}
958
// check whether absolute or relative filenames are given
959
if (!FileHelpers::isAbsolute(file)) {
960
return FileHelpers::getConfigurationRelative(base, file);
961
}
962
return file;
963
}
964
965
966
MSLane*
967
NLTriggerBuilder::getLane(const SUMOSAXAttributes& attrs,
968
const std::string& tt,
969
const std::string& tid) {
970
bool ok = true;
971
std::string objectid = attrs.get<std::string>(SUMO_ATTR_LANE, tid.c_str(), ok);
972
MSLane* lane = MSLane::dictionary(objectid);
973
if (lane == nullptr) {
974
// Either a lane that is non-existent/broken, or a lane that is internal and has been ignored.
975
// We assume that internal lane names start with ':'.
976
if (objectid[0] == ':' && !MSGlobals::gUsingInternalLanes) {
977
return nullptr;
978
}
979
// Throw the exception only in case that the lane really does not exist in the network file
980
// or it is broken.
981
throw InvalidArgument("The lane " + objectid + " to use within the " + tt + " '" + tid + "' is not known.");
982
}
983
return lane;
984
}
985
986
987
MSParkingArea*
988
NLTriggerBuilder::getParkingArea(const SUMOSAXAttributes& attrs, const std::string& tt, const std::string& tid) {
989
bool ok = true;
990
std::string objectID = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, tid.c_str(), ok);
991
if (!ok || objectID.size() == 0) {
992
return nullptr;
993
}
994
MSParkingArea* pa = static_cast<MSParkingArea*>(MSNet::getInstance()->getStoppingPlace(objectID, SUMO_TAG_PARKING_AREA));
995
if (pa == nullptr) {
996
// Throw the exception only in case that the lane really does not exist in the network file
997
// or it is broken.
998
throw InvalidArgument("The parkingArea " + objectID + " to use within the " + tt + " '" + tid + "' is not known.");
999
}
1000
return pa;
1001
}
1002
1003
1004
double
1005
NLTriggerBuilder::getPosition(const SUMOSAXAttributes& attrs,
1006
MSLane* lane,
1007
const std::string& tt, const std::string& tid,
1008
MSEdge* edge) {
1009
assert(lane != nullptr || edge != nullptr);
1010
const double length = lane != nullptr ? lane->getLength() : edge->getLength();
1011
bool ok = true;
1012
double pos = attrs.get<double>(SUMO_ATTR_POSITION, nullptr, ok);
1013
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, false);
1014
if (!ok) {
1015
throw InvalidArgument("Error on parsing a position information.");
1016
}
1017
if (pos < 0) {
1018
pos = length + pos;
1019
}
1020
if (pos > length) {
1021
if (friendlyPos) {
1022
pos = length - (double) 0.1;
1023
} else {
1024
if (lane != nullptr) {
1025
throw InvalidArgument("The position of " + tt + " '" + tid + "' lies beyond the lane's '" + lane->getID() + "' length.");
1026
} else {
1027
throw InvalidArgument("The position of " + tt + " '" + tid + "' lies beyond the edge's '" + edge->getID() + "' length.");
1028
}
1029
}
1030
}
1031
return pos;
1032
}
1033
1034
1035
void
1036
NLTriggerBuilder::updateParkingAreaDefaultCapacity() {
1037
if (myParkingArea != nullptr && !myParkingAreaCapacitySet) {
1038
myParkingArea->setRoadsideCapacity(1);
1039
}
1040
}
1041
1042
1043
MSStoppingPlace*
1044
NLTriggerBuilder::getCurrentStop() {
1045
return myParkingArea == nullptr ? myCurrentStop : myParkingArea;
1046
}
1047
1048
1049
/****************************************************************************/
1050
1051