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