Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netload/NLBuilder.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 NLBuilder.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Michael Behrisch
18
/// @date Mon, 9 Jul 2001
19
///
20
// The main interface for loading a microsim
21
/****************************************************************************/
22
#include <config.h>
23
24
#include <iostream>
25
#include <vector>
26
#include <string>
27
#include <map>
28
29
#include <utils/common/MsgHandler.h>
30
#include <utils/common/StringTokenizer.h>
31
#include <utils/common/SystemFrame.h>
32
#include <utils/options/Option.h>
33
#include <utils/options/OptionsCont.h>
34
#include <utils/options/OptionsIO.h>
35
#include <utils/common/StringUtils.h>
36
#include <utils/common/FileHelpers.h>
37
#include <utils/common/SysUtils.h>
38
#include <utils/common/ToString.h>
39
#include <utils/vehicle/SUMORouteLoaderControl.h>
40
#include <utils/vehicle/SUMORouteLoader.h>
41
#include <utils/xml/XMLSubSys.h>
42
#ifdef HAVE_FOX
43
#include <utils/foxtools/MsgHandlerSynchronized.h>
44
#endif
45
#include <libsumo/Helper.h>
46
#include <mesosim/MEVehicleControl.h>
47
#include <microsim/MSVehicleControl.h>
48
#include <microsim/MSVehicleTransfer.h>
49
#include <microsim/MSNet.h>
50
#include <microsim/devices/MSDevice.h>
51
#include <microsim/devices/MSDevice_ToC.h>
52
#include <microsim/devices/MSDevice_BTreceiver.h>
53
#include <microsim/devices/MSDevice_FCDReplay.h>
54
#include <microsim/MSEdgeControl.h>
55
#include <microsim/MSGlobals.h>
56
#include <microsim/output/MSDetectorControl.h>
57
#include <microsim/MSFrame.h>
58
#include <microsim/MSEdgeWeightsStorage.h>
59
#include <microsim/MSStateHandler.h>
60
#include <microsim/MSDriverState.h>
61
#include <microsim/trigger/MSTriggeredRerouter.h>
62
#include <traci-server/TraCIServer.h>
63
64
#include "NLHandler.h"
65
#include "NLNetShapeHandler.h"
66
#include "NLEdgeControlBuilder.h"
67
#include "NLJunctionControlBuilder.h"
68
#include "NLDetectorBuilder.h"
69
#include "NLTriggerBuilder.h"
70
#include "NLBuilder.h"
71
72
73
// ===========================================================================
74
// method definitions
75
// ===========================================================================
76
// ---------------------------------------------------------------------------
77
// NLBuilder::EdgeFloatTimeLineRetriever_EdgeWeight - methods
78
// ---------------------------------------------------------------------------
79
void
80
NLBuilder::EdgeFloatTimeLineRetriever_EdgeEffort::addEdgeWeight(const std::string& id,
81
double value, double begTime, double endTime) const {
82
MSEdge* edge = MSEdge::dictionary(id);
83
if (edge != nullptr) {
84
myNet.getWeightsStorage().addEffort(edge, begTime, endTime, value);
85
} else {
86
WRITE_ERRORF(TL("Trying to set the effort for the unknown edge '%'."), id);
87
}
88
}
89
90
91
// ---------------------------------------------------------------------------
92
// NLBuilder::EdgeFloatTimeLineRetriever_EdgeTravelTime - methods
93
// ---------------------------------------------------------------------------
94
void
95
NLBuilder::EdgeFloatTimeLineRetriever_EdgeTravelTime::addEdgeWeight(const std::string& id,
96
double value, double begTime, double endTime) const {
97
MSEdge* edge = MSEdge::dictionary(id);
98
if (edge != nullptr) {
99
myNet.getWeightsStorage().addTravelTime(edge, begTime, endTime, value);
100
} else {
101
WRITE_ERRORF(TL("Trying to set the travel time for the unknown edge '%'."), id);
102
}
103
}
104
105
106
// ---------------------------------------------------------------------------
107
// NLBuilder - methods
108
// ---------------------------------------------------------------------------
109
NLBuilder::NLBuilder(OptionsCont& oc,
110
MSNet& net,
111
NLEdgeControlBuilder& eb,
112
NLJunctionControlBuilder& jb,
113
NLDetectorBuilder& db,
114
NLHandler& xmlHandler)
115
: myOptions(oc), myEdgeBuilder(eb), myJunctionBuilder(jb),
116
myDetectorBuilder(db),
117
myNet(net), myXMLHandler(xmlHandler) {}
118
119
120
NLBuilder::~NLBuilder() {}
121
122
123
bool
124
NLBuilder::build() {
125
// try to build the net
126
if (!load("net-file", true)) {
127
return false;
128
}
129
if (myXMLHandler.networkVersion() == MMVersion(0, 0)) {
130
throw ProcessError(TL("Invalid network, no network version declared."));
131
}
132
// check whether the loaded net agrees with the simulation options
133
if ((myOptions.getBool("no-internal-links") || myOptions.getBool("mesosim")) && myXMLHandler.haveSeenInternalEdge() && myXMLHandler.haveSeenDefaultLength()) {
134
WRITE_WARNING(TL("Network contains internal links which are ignored. Vehicles will 'jump' across junctions and thus underestimate route lengths and travel times."));
135
}
136
buildNet();
137
if (myOptions.isSet("alternative-net-file")) {
138
for (std::string fname : myOptions.getStringVector("alternative-net-file")) {
139
const long before = PROGRESS_BEGIN_TIME_MESSAGE("Loading alternative net from '" + fname + "'");
140
NLNetShapeHandler nsh(fname, myNet);
141
if (!XMLSubSys::runParser(nsh, fname, true)) {
142
WRITE_MESSAGE("Loading of alternative net failed.");
143
return false;
144
}
145
nsh.sortInternalShapes();
146
PROGRESS_TIME_MESSAGE(before);
147
}
148
}
149
// @note on loading order constraints:
150
// - additional-files before route-files and state-files due to referencing
151
// - additional-files before weight-files since the latter might contain intermodal edge data and the intermodal net depends on the stops and public transport from the additionals
152
153
bool stateBeginMismatch = false;
154
if (myOptions.isSet("load-state")) {
155
// first, load only the time
156
const SUMOTime stateTime = MSStateHandler::MSStateTimeHandler::getTime(myOptions.getString("load-state"));
157
if (myOptions.isDefault("begin")) {
158
myOptions.set("begin", time2string(stateTime));
159
if (TraCIServer::getInstance() != nullptr) {
160
TraCIServer::getInstance()->stateLoaded(stateTime);
161
}
162
} else {
163
if (stateTime != string2time(myOptions.getString("begin"))) {
164
WRITE_WARNINGF(TL("State was written at a different time=% than the begin time %!"), time2string(stateTime), myOptions.getString("begin"));
165
stateBeginMismatch = true;
166
}
167
}
168
}
169
170
if (myOptions.getBool("junction-taz")) {
171
// create a TAZ for every junction
172
const MSJunctionControl& junctions = myNet.getJunctionControl();
173
for (auto it = junctions.begin(); it != junctions.end(); it++) {
174
const std::string sinkID = it->first + "-sink";
175
const std::string sourceID = it->first + "-source";
176
if (MSEdge::dictionary(sinkID) == nullptr && MSEdge::dictionary(sourceID) == nullptr) {
177
// sink must be built and added before source
178
MSEdge* sink = myEdgeBuilder.buildEdge(sinkID, SumoXMLEdgeFunc::CONNECTOR, "", "", -1, 0);
179
MSEdge* source = myEdgeBuilder.buildEdge(sourceID, SumoXMLEdgeFunc::CONNECTOR, "", "", -1, 0);
180
sink->setOtherTazConnector(source);
181
source->setOtherTazConnector(sink);
182
MSEdge::dictionary(sinkID, sink);
183
MSEdge::dictionary(sourceID, source);
184
sink->initialize(new std::vector<MSLane*>());
185
source->initialize(new std::vector<MSLane*>());
186
const MSJunction* junction = it->second;
187
for (const MSEdge* edge : junction->getIncoming()) {
188
if (!edge->isInternal()) {
189
const_cast<MSEdge*>(edge)->addSuccessor(sink);
190
}
191
}
192
for (const MSEdge* edge : junction->getOutgoing()) {
193
if (!edge->isInternal()) {
194
source->addSuccessor(const_cast<MSEdge*>(edge));
195
}
196
}
197
} else {
198
WRITE_WARNINGF(TL("A TAZ with id '%' already exists. Not building junction TAZ."), it->first)
199
}
200
}
201
}
202
203
// load additional net elements (sources, detectors, ...)
204
if (myOptions.isSet("additional-files")) {
205
if (!load("additional-files")) {
206
return false;
207
}
208
// load shapes with separate handler
209
NLShapeHandler sh("", myNet.getShapeContainer());
210
if (!ShapeHandler::loadFiles(myOptions.getStringVector("additional-files"), sh)) {
211
return false;
212
}
213
if (myXMLHandler.haveSeenAdditionalSpeedRestrictions()) {
214
myNet.getEdgeControl().setAdditionalRestrictions();
215
}
216
if (MSGlobals::gUseMesoSim && (myXMLHandler.haveSeenMesoEdgeType() || myXMLHandler.haveSeenTLSParams())) {
217
myNet.getEdgeControl().setMesoTypes();
218
for (MSTrafficLightLogic* tll : myNet.getTLSControl().getAllLogics()) {
219
tll->initMesoTLSPenalties();
220
}
221
}
222
MSTriggeredRerouter::checkParkingRerouteConsistency();
223
}
224
// init tls after all detectors have been loaded
225
myJunctionBuilder.postLoadInitialization();
226
// declare meandata set by options
227
buildDefaultMeanData("edgedata-output", "DEFAULT_EDGEDATA", false);
228
buildDefaultMeanData("lanedata-output", "DEFAULT_LANEDATA", true);
229
230
if (stateBeginMismatch && myNet.getVehicleControl().getLoadedVehicleNo() > 0) {
231
throw ProcessError(TL("Loading vehicles ahead of a state file is not supported. Correct --begin option or load vehicles with option --route-files"));
232
}
233
234
// load weights if wished
235
if (myOptions.isSet("weight-files")) {
236
if (!myOptions.isUsableFileList("weight-files")) {
237
return false;
238
}
239
// build and prepare the weights handler
240
std::vector<SAXWeightsHandler::ToRetrieveDefinition*> retrieverDefs;
241
// travel time, first (always used)
242
EdgeFloatTimeLineRetriever_EdgeTravelTime ttRetriever(myNet);
243
retrieverDefs.push_back(new SAXWeightsHandler::ToRetrieveDefinition("traveltime", true, ttRetriever));
244
// the measure to use, then
245
EdgeFloatTimeLineRetriever_EdgeEffort eRetriever(myNet);
246
std::string measure = myOptions.getString("weight-attribute");
247
if (!myOptions.isDefault("weight-attribute")) {
248
if (measure == "CO" || measure == "CO2" || measure == "HC" || measure == "PMx" || measure == "NOx" || measure == "fuel" || measure == "electricity") {
249
measure += "_perVeh";
250
}
251
retrieverDefs.push_back(new SAXWeightsHandler::ToRetrieveDefinition(measure, true, eRetriever));
252
}
253
// set up handler
254
SAXWeightsHandler handler(retrieverDefs, "");
255
// start parsing; for each file in the list
256
std::vector<std::string> files = myOptions.getStringVector("weight-files");
257
for (std::vector<std::string>::iterator i = files.begin(); i != files.end(); ++i) {
258
// report about loading when wished
259
WRITE_MESSAGEF(TL("Loading weights from '%'..."), *i);
260
// parse the file
261
if (!XMLSubSys::runParser(handler, *i)) {
262
return false;
263
}
264
}
265
}
266
// load the previous state if wished
267
if (myOptions.isSet("load-state")) {
268
myNet.setCurrentTimeStep(string2time(myOptions.getString("begin")));
269
const std::string& f = myOptions.getString("load-state");
270
long before = PROGRESS_BEGIN_TIME_MESSAGE(TLF("Loading state from '%'", f));
271
MSStateHandler h(f, string2time(myOptions.getString("load-state.offset")));
272
XMLSubSys::runParser(h, f);
273
if (MsgHandler::getErrorInstance()->wasInformed()) {
274
return false;
275
}
276
PROGRESS_TIME_MESSAGE(before);
277
}
278
// routes from FCD files
279
MSDevice_FCDReplay::init();
280
// load routes
281
if (myOptions.isSet("route-files")) {
282
if (string2time(myOptions.getString("route-steps")) <= 0) {
283
// incremental loading is disabled. Load route files fully
284
if (!load("route-files")) {
285
return false;
286
}
287
} else {
288
// message must come after additional-files have been loaded (but buildRouteLoaderControl was called earlier)
289
for (std::string file : myOptions.getStringVector("route-files")) {
290
WRITE_MESSAGE(TLF("Loading route-files incrementally from '%'", file));
291
}
292
}
293
}
294
// optionally switch off traffic lights
295
if (myOptions.getBool("tls.all-off")) {
296
myNet.getTLSControl().switchOffAll();
297
}
298
WRITE_MESSAGE(TL("Loading done."));
299
return true;
300
}
301
302
303
MSNet*
304
NLBuilder::init(const bool isLibsumo) {
305
OptionsCont& oc = OptionsCont::getOptions();
306
oc.clear();
307
MSFrame::fillOptions();
308
OptionsIO::getOptions();
309
if (oc.processMetaOptions(OptionsIO::getArgC() < 2)) {
310
SystemFrame::close();
311
return nullptr;
312
}
313
SystemFrame::checkOptions(oc);
314
std::string validation = oc.getString("xml-validation");
315
std::string routeValidation = oc.getString("xml-validation.routes");
316
if (isLibsumo) {
317
if (oc.isDefault("xml-validation")) {
318
validation = "never";
319
}
320
if (oc.isDefault("xml-validation.routes")) {
321
routeValidation = "never";
322
}
323
}
324
XMLSubSys::setValidation(validation, oc.getString("xml-validation.net"), routeValidation);
325
if (!MSFrame::checkOptions()) {
326
throw ProcessError();
327
}
328
#ifdef HAVE_FOX
329
if (oc.getInt("threads") > 1) {
330
// make the output aware of threading
331
MsgHandler::setFactory(&MsgHandlerSynchronized::create);
332
}
333
#endif
334
MsgHandler::initOutputOptions();
335
initRandomness();
336
MSFrame::setMSGlobals(oc);
337
MSVehicleControl* vc = nullptr;
338
if (MSGlobals::gUseMesoSim) {
339
vc = new MEVehicleControl();
340
} else {
341
vc = new MSVehicleControl();
342
}
343
MSNet* net = new MSNet(vc, new MSEventControl(), new MSEventControl(), new MSEventControl());
344
// need to init TraCI-Server before loading routes to catch VehicleState::BUILT
345
TraCIServer::openSocket(std::map<int, TraCIServer::CmdExecutor>());
346
if (isLibsumo) {
347
libsumo::Helper::registerStateListener();
348
}
349
350
NLEdgeControlBuilder eb;
351
NLDetectorBuilder db(*net);
352
NLJunctionControlBuilder jb(*net, db);
353
NLTriggerBuilder tb;
354
NLHandler handler("", *net, db, tb, eb, jb);
355
tb.setHandler(&handler);
356
NLBuilder builder(oc, *net, eb, jb, db, handler);
357
MsgHandler::getErrorInstance()->clear();
358
MsgHandler::getWarningInstance()->clear();
359
MsgHandler::getMessageInstance()->clear();
360
if (builder.build()) {
361
// preload the routes especially for TraCI
362
net->loadRoutes();
363
return net;
364
}
365
delete net;
366
throw ProcessError();
367
}
368
369
370
void
371
NLBuilder::initRandomness() {
372
RandHelper::initRandGlobal();
373
RandHelper::initRandGlobal(MSRouteHandler::getParsingRNG());
374
RandHelper::initRandGlobal(MSDevice::getEquipmentRNG());
375
RandHelper::initRandGlobal(OUProcess::getRNG());
376
RandHelper::initRandGlobal(MSDevice_ToC::getResponseTimeRNG());
377
RandHelper::initRandGlobal(MSDevice_BTreceiver::getRecognitionRNG());
378
MSLane::initRNGs(OptionsCont::getOptions());
379
}
380
381
382
void
383
NLBuilder::buildNet() {
384
MSEdgeControl* edges = nullptr;
385
MSJunctionControl* junctions = nullptr;
386
SUMORouteLoaderControl* routeLoaders = nullptr;
387
MSTLLogicControl* tlc = nullptr;
388
std::vector<SUMOTime> stateDumpTimes;
389
std::vector<std::string> stateDumpFiles;
390
try {
391
MSFrame::buildStreams(); // ensure streams are ready for output during building
392
edges = myEdgeBuilder.build(myXMLHandler.networkVersion());
393
junctions = myJunctionBuilder.build();
394
junctions->postloadInitContainer();
395
routeLoaders = buildRouteLoaderControl(myOptions);
396
tlc = myJunctionBuilder.buildTLLogics();
397
for (std::string timeStr : myOptions.getStringVector("save-state.times")) {
398
stateDumpTimes.push_back(string2time(timeStr));
399
}
400
if (myOptions.isSet("save-state.files")) {
401
stateDumpFiles = myOptions.getStringVector("save-state.files");
402
if (stateDumpFiles.size() != stateDumpTimes.size()) {
403
throw ProcessError(TL("Wrong number of state file names!"));
404
}
405
} else {
406
const std::string prefix = myOptions.getString("save-state.prefix");
407
const std::string suffix = myOptions.getString("save-state.suffix");
408
for (std::vector<SUMOTime>::iterator i = stateDumpTimes.begin(); i != stateDumpTimes.end(); ++i) {
409
std::string timeStamp = time2string(*i);
410
std::replace(timeStamp.begin(), timeStamp.end(), ':', '-');
411
stateDumpFiles.push_back(prefix + "_" + timeStamp + suffix);
412
}
413
}
414
} catch (ProcessError&) {
415
MSEdge::clear();
416
MSLane::clear();
417
delete edges;
418
delete junctions;
419
delete routeLoaders;
420
delete tlc;
421
throw;
422
}
423
// if anthing goes wrong after this point, the net is responsible for cleaning up
424
myNet.closeBuilding(myOptions, edges, junctions, routeLoaders, tlc, stateDumpTimes, stateDumpFiles,
425
myXMLHandler.haveSeenInternalEdge(),
426
myXMLHandler.hasJunctionHigherSpeeds(),
427
myXMLHandler.networkVersion());
428
}
429
430
431
bool
432
NLBuilder::load(const std::string& mmlWhat, const bool isNet) {
433
if (!myOptions.isUsableFileList(mmlWhat)) {
434
return false;
435
}
436
std::vector<std::string> files = myOptions.getStringVector(mmlWhat);
437
for (std::vector<std::string>::const_iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
438
const long before = PROGRESS_BEGIN_TIME_MESSAGE(TLF("Loading % from '%'", mmlWhat, *fileIt));
439
if (!XMLSubSys::runParser(myXMLHandler, *fileIt, isNet)) {
440
WRITE_MESSAGEF(TL("Loading of % failed."), mmlWhat);
441
return false;
442
}
443
PROGRESS_TIME_MESSAGE(before);
444
}
445
return true;
446
}
447
448
449
SUMORouteLoaderControl*
450
NLBuilder::buildRouteLoaderControl(const OptionsCont& oc) {
451
// build the loaders
452
SUMORouteLoaderControl* loaders = new SUMORouteLoaderControl(string2time(oc.getString("route-steps")));
453
// check whether a list is existing
454
if (oc.isSet("route-files") && string2time(oc.getString("route-steps")) > 0) {
455
std::vector<std::string> files = oc.getStringVector("route-files");
456
for (std::vector<std::string>::const_iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
457
if (!FileHelpers::isReadable(*fileIt)) {
458
throw ProcessError(TLF("The route file '%' is not accessible.", *fileIt));
459
}
460
}
461
// open files for reading
462
for (std::vector<std::string>::const_iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
463
loaders->add(new SUMORouteLoader(new MSRouteHandler(*fileIt, false)));
464
}
465
}
466
return loaders;
467
}
468
469
470
void
471
NLBuilder::buildDefaultMeanData(const std::string& optionName, const std::string& id, bool useLanes) {
472
if (OptionsCont::getOptions().isSet(optionName)) {
473
if (useLanes && MSGlobals::gUseMesoSim && !OptionsCont::getOptions().getBool("meso-lane-queue")) {
474
WRITE_WARNING(TL("LaneData requested for mesoscopic simulation but --meso-lane-queue is not active. Falling back to edgeData."));
475
useLanes = false;
476
}
477
try {
478
SUMOTime begin = string2time(OptionsCont::getOptions().getString("begin"));
479
myDetectorBuilder.createEdgeLaneMeanData(id, -1, begin, -1, "traffic", useLanes, false, false,
480
false, false, false, 100000, 0, SUMO_const_haltingSpeed, "", "", std::vector<MSEdge*>(), false,
481
OptionsCont::getOptions().getString(optionName));
482
} catch (InvalidArgument& e) {
483
WRITE_ERROR(e.what());
484
} catch (IOError& e) {
485
WRITE_ERROR(e.what());
486
}
487
}
488
}
489
490
/****************************************************************************/
491
492