#include <config.h>
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <utils/common/MsgHandler.h>
#include <utils/common/StringTokenizer.h>
#include <utils/common/SystemFrame.h>
#include <utils/options/Option.h>
#include <utils/options/OptionsCont.h>
#include <utils/options/OptionsIO.h>
#include <utils/common/StringUtils.h>
#include <utils/common/FileHelpers.h>
#include <utils/common/SysUtils.h>
#include <utils/common/ToString.h>
#include <utils/vehicle/SUMORouteLoaderControl.h>
#include <utils/vehicle/SUMORouteLoader.h>
#include <utils/xml/XMLSubSys.h>
#ifdef HAVE_FOX
#include <utils/foxtools/MsgHandlerSynchronized.h>
#endif
#include <libsumo/Helper.h>
#include <mesosim/MEVehicleControl.h>
#include <microsim/MSVehicleControl.h>
#include <microsim/MSVehicleTransfer.h>
#include <microsim/MSNet.h>
#include <microsim/devices/MSDevice.h>
#include <microsim/devices/MSDevice_ToC.h>
#include <microsim/devices/MSDevice_BTreceiver.h>
#include <microsim/devices/MSDevice_FCDReplay.h>
#include <microsim/MSEdgeControl.h>
#include <microsim/MSGlobals.h>
#include <microsim/output/MSDetectorControl.h>
#include <microsim/MSFrame.h>
#include <microsim/MSEdgeWeightsStorage.h>
#include <microsim/MSStateHandler.h>
#include <microsim/MSDriverState.h>
#include <microsim/trigger/MSTriggeredRerouter.h>
#include <traci-server/TraCIServer.h>
#include "NLHandler.h"
#include "NLNetShapeHandler.h"
#include "NLEdgeControlBuilder.h"
#include "NLJunctionControlBuilder.h"
#include "NLDetectorBuilder.h"
#include "NLTriggerBuilder.h"
#include "NLBuilder.h"
void
NLBuilder::EdgeFloatTimeLineRetriever_EdgeEffort::addEdgeWeight(const std::string& id,
double value, double begTime, double endTime) const {
MSEdge* edge = MSEdge::dictionary(id);
if (edge != nullptr) {
myNet.getWeightsStorage().addEffort(edge, begTime, endTime, value);
} else {
WRITE_ERRORF(TL("Trying to set the effort for the unknown edge '%'."), id);
}
}
void
NLBuilder::EdgeFloatTimeLineRetriever_EdgeTravelTime::addEdgeWeight(const std::string& id,
double value, double begTime, double endTime) const {
MSEdge* edge = MSEdge::dictionary(id);
if (edge != nullptr) {
myNet.getWeightsStorage().addTravelTime(edge, begTime, endTime, value);
} else {
WRITE_ERRORF(TL("Trying to set the travel time for the unknown edge '%'."), id);
}
}
NLBuilder::NLBuilder(OptionsCont& oc,
MSNet& net,
NLEdgeControlBuilder& eb,
NLJunctionControlBuilder& jb,
NLDetectorBuilder& db,
NLHandler& xmlHandler)
: myOptions(oc), myEdgeBuilder(eb), myJunctionBuilder(jb),
myDetectorBuilder(db),
myNet(net), myXMLHandler(xmlHandler) {}
NLBuilder::~NLBuilder() {}
bool
NLBuilder::build() {
if (!load("net-file", true)) {
return false;
}
if (myXMLHandler.networkVersion() == MMVersion(0, 0)) {
throw ProcessError(TL("Invalid network, no network version declared."));
}
if ((myOptions.getBool("no-internal-links") || myOptions.getBool("mesosim")) && myXMLHandler.haveSeenInternalEdge() && myXMLHandler.haveSeenDefaultLength()) {
WRITE_WARNING(TL("Network contains internal links which are ignored. Vehicles will 'jump' across junctions and thus underestimate route lengths and travel times."));
}
buildNet();
if (myOptions.isSet("alternative-net-file")) {
for (std::string fname : myOptions.getStringVector("alternative-net-file")) {
const long before = PROGRESS_BEGIN_TIME_MESSAGE("Loading alternative net from '" + fname + "'");
NLNetShapeHandler nsh(fname, myNet);
if (!XMLSubSys::runParser(nsh, fname, true)) {
WRITE_MESSAGE("Loading of alternative net failed.");
return false;
}
nsh.sortInternalShapes();
PROGRESS_TIME_MESSAGE(before);
}
}
bool stateBeginMismatch = false;
if (myOptions.isSet("load-state")) {
const SUMOTime stateTime = MSStateHandler::MSStateTimeHandler::getTime(myOptions.getString("load-state"));
if (myOptions.isDefault("begin")) {
myOptions.set("begin", time2string(stateTime));
if (TraCIServer::getInstance() != nullptr) {
TraCIServer::getInstance()->stateLoaded(stateTime);
}
} else {
if (stateTime != string2time(myOptions.getString("begin"))) {
WRITE_WARNINGF(TL("State was written at a different time=% than the begin time %!"), time2string(stateTime), myOptions.getString("begin"));
stateBeginMismatch = true;
}
}
}
if (myOptions.getBool("junction-taz")) {
const MSJunctionControl& junctions = myNet.getJunctionControl();
for (auto it = junctions.begin(); it != junctions.end(); it++) {
const std::string sinkID = it->first + "-sink";
const std::string sourceID = it->first + "-source";
if (MSEdge::dictionary(sinkID) == nullptr && MSEdge::dictionary(sourceID) == nullptr) {
MSEdge* sink = myEdgeBuilder.buildEdge(sinkID, SumoXMLEdgeFunc::CONNECTOR, "", "", -1, 0);
MSEdge* source = myEdgeBuilder.buildEdge(sourceID, SumoXMLEdgeFunc::CONNECTOR, "", "", -1, 0);
sink->setOtherTazConnector(source);
source->setOtherTazConnector(sink);
MSEdge::dictionary(sinkID, sink);
MSEdge::dictionary(sourceID, source);
sink->initialize(new std::vector<MSLane*>());
source->initialize(new std::vector<MSLane*>());
const MSJunction* junction = it->second;
for (const MSEdge* edge : junction->getIncoming()) {
if (!edge->isInternal()) {
const_cast<MSEdge*>(edge)->addSuccessor(sink);
}
}
for (const MSEdge* edge : junction->getOutgoing()) {
if (!edge->isInternal()) {
source->addSuccessor(const_cast<MSEdge*>(edge));
}
}
} else {
WRITE_WARNINGF(TL("A TAZ with id '%' already exists. Not building junction TAZ."), it->first)
}
}
}
if (myOptions.isSet("additional-files")) {
if (!load("additional-files")) {
return false;
}
NLShapeHandler sh("", myNet.getShapeContainer());
if (!ShapeHandler::loadFiles(myOptions.getStringVector("additional-files"), sh)) {
return false;
}
if (myXMLHandler.haveSeenAdditionalSpeedRestrictions()) {
myNet.getEdgeControl().setAdditionalRestrictions();
}
if (MSGlobals::gUseMesoSim && (myXMLHandler.haveSeenMesoEdgeType() || myXMLHandler.haveSeenTLSParams())) {
myNet.getEdgeControl().setMesoTypes();
for (MSTrafficLightLogic* tll : myNet.getTLSControl().getAllLogics()) {
tll->initMesoTLSPenalties();
}
}
MSTriggeredRerouter::checkParkingRerouteConsistency();
}
myJunctionBuilder.postLoadInitialization();
buildDefaultMeanData("edgedata-output", "DEFAULT_EDGEDATA", false);
buildDefaultMeanData("lanedata-output", "DEFAULT_LANEDATA", true);
if (stateBeginMismatch && myNet.getVehicleControl().getLoadedVehicleNo() > 0) {
throw ProcessError(TL("Loading vehicles ahead of a state file is not supported. Correct --begin option or load vehicles with option --route-files"));
}
if (myOptions.isSet("weight-files")) {
if (!myOptions.isUsableFileList("weight-files")) {
return false;
}
std::vector<SAXWeightsHandler::ToRetrieveDefinition*> retrieverDefs;
EdgeFloatTimeLineRetriever_EdgeTravelTime ttRetriever(myNet);
retrieverDefs.push_back(new SAXWeightsHandler::ToRetrieveDefinition("traveltime", true, ttRetriever));
EdgeFloatTimeLineRetriever_EdgeEffort eRetriever(myNet);
std::string measure = myOptions.getString("weight-attribute");
if (!myOptions.isDefault("weight-attribute")) {
if (measure == "CO" || measure == "CO2" || measure == "HC" || measure == "PMx" || measure == "NOx" || measure == "fuel" || measure == "electricity") {
measure += "_perVeh";
}
retrieverDefs.push_back(new SAXWeightsHandler::ToRetrieveDefinition(measure, true, eRetriever));
}
SAXWeightsHandler handler(retrieverDefs, "");
std::vector<std::string> files = myOptions.getStringVector("weight-files");
for (std::vector<std::string>::iterator i = files.begin(); i != files.end(); ++i) {
WRITE_MESSAGEF(TL("Loading weights from '%'..."), *i);
if (!XMLSubSys::runParser(handler, *i)) {
return false;
}
}
}
if (myOptions.isSet("load-state")) {
myNet.setCurrentTimeStep(string2time(myOptions.getString("begin")));
const std::string& f = myOptions.getString("load-state");
long before = PROGRESS_BEGIN_TIME_MESSAGE(TLF("Loading state from '%'", f));
MSStateHandler h(f, string2time(myOptions.getString("load-state.offset")));
XMLSubSys::runParser(h, f);
if (MsgHandler::getErrorInstance()->wasInformed()) {
return false;
}
PROGRESS_TIME_MESSAGE(before);
}
MSDevice_FCDReplay::init();
if (myOptions.isSet("route-files")) {
if (string2time(myOptions.getString("route-steps")) <= 0) {
if (!load("route-files")) {
return false;
}
} else {
for (std::string file : myOptions.getStringVector("route-files")) {
WRITE_MESSAGE(TLF("Loading route-files incrementally from '%'", file));
}
}
}
if (myOptions.getBool("tls.all-off")) {
myNet.getTLSControl().switchOffAll();
}
WRITE_MESSAGE(TL("Loading done."));
return true;
}
MSNet*
NLBuilder::init(const bool isLibsumo) {
OptionsCont& oc = OptionsCont::getOptions();
oc.clear();
MSFrame::fillOptions();
OptionsIO::getOptions();
if (oc.processMetaOptions(OptionsIO::getArgC() < 2)) {
SystemFrame::close();
return nullptr;
}
SystemFrame::checkOptions(oc);
std::string validation = oc.getString("xml-validation");
std::string routeValidation = oc.getString("xml-validation.routes");
if (isLibsumo) {
if (oc.isDefault("xml-validation")) {
validation = "never";
}
if (oc.isDefault("xml-validation.routes")) {
routeValidation = "never";
}
}
XMLSubSys::setValidation(validation, oc.getString("xml-validation.net"), routeValidation);
if (!MSFrame::checkOptions()) {
throw ProcessError();
}
#ifdef HAVE_FOX
if (oc.getInt("threads") > 1) {
MsgHandler::setFactory(&MsgHandlerSynchronized::create);
}
#endif
MsgHandler::initOutputOptions();
initRandomness();
MSFrame::setMSGlobals(oc);
MSVehicleControl* vc = nullptr;
if (MSGlobals::gUseMesoSim) {
vc = new MEVehicleControl();
} else {
vc = new MSVehicleControl();
}
MSNet* net = new MSNet(vc, new MSEventControl(), new MSEventControl(), new MSEventControl());
TraCIServer::openSocket(std::map<int, TraCIServer::CmdExecutor>());
if (isLibsumo) {
libsumo::Helper::registerStateListener();
}
NLEdgeControlBuilder eb;
NLDetectorBuilder db(*net);
NLJunctionControlBuilder jb(*net, db);
NLTriggerBuilder tb;
NLHandler handler("", *net, db, tb, eb, jb);
tb.setHandler(&handler);
NLBuilder builder(oc, *net, eb, jb, db, handler);
MsgHandler::getErrorInstance()->clear();
MsgHandler::getWarningInstance()->clear();
MsgHandler::getMessageInstance()->clear();
if (builder.build()) {
net->loadRoutes();
return net;
}
delete net;
throw ProcessError();
}
void
NLBuilder::initRandomness() {
RandHelper::initRandGlobal();
RandHelper::initRandGlobal(MSRouteHandler::getParsingRNG());
RandHelper::initRandGlobal(MSDevice::getEquipmentRNG());
RandHelper::initRandGlobal(OUProcess::getRNG());
RandHelper::initRandGlobal(MSDevice_ToC::getResponseTimeRNG());
RandHelper::initRandGlobal(MSDevice_BTreceiver::getRecognitionRNG());
MSLane::initRNGs(OptionsCont::getOptions());
}
void
NLBuilder::buildNet() {
MSEdgeControl* edges = nullptr;
MSJunctionControl* junctions = nullptr;
SUMORouteLoaderControl* routeLoaders = nullptr;
MSTLLogicControl* tlc = nullptr;
std::vector<SUMOTime> stateDumpTimes;
std::vector<std::string> stateDumpFiles;
try {
MSFrame::buildStreams();
edges = myEdgeBuilder.build(myXMLHandler.networkVersion());
junctions = myJunctionBuilder.build();
junctions->postloadInitContainer();
routeLoaders = buildRouteLoaderControl(myOptions);
tlc = myJunctionBuilder.buildTLLogics();
for (std::string timeStr : myOptions.getStringVector("save-state.times")) {
stateDumpTimes.push_back(string2time(timeStr));
}
if (myOptions.isSet("save-state.files")) {
stateDumpFiles = myOptions.getStringVector("save-state.files");
if (stateDumpFiles.size() != stateDumpTimes.size()) {
throw ProcessError(TL("Wrong number of state file names!"));
}
} else {
const std::string prefix = myOptions.getString("save-state.prefix");
const std::string suffix = myOptions.getString("save-state.suffix");
for (std::vector<SUMOTime>::iterator i = stateDumpTimes.begin(); i != stateDumpTimes.end(); ++i) {
std::string timeStamp = time2string(*i);
std::replace(timeStamp.begin(), timeStamp.end(), ':', '-');
stateDumpFiles.push_back(prefix + "_" + timeStamp + suffix);
}
}
} catch (ProcessError&) {
MSEdge::clear();
MSLane::clear();
delete edges;
delete junctions;
delete routeLoaders;
delete tlc;
throw;
}
myNet.closeBuilding(myOptions, edges, junctions, routeLoaders, tlc, stateDumpTimes, stateDumpFiles,
myXMLHandler.haveSeenInternalEdge(),
myXMLHandler.hasJunctionHigherSpeeds(),
myXMLHandler.networkVersion());
}
bool
NLBuilder::load(const std::string& mmlWhat, const bool isNet) {
if (!myOptions.isUsableFileList(mmlWhat)) {
return false;
}
std::vector<std::string> files = myOptions.getStringVector(mmlWhat);
for (std::vector<std::string>::const_iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
const long before = PROGRESS_BEGIN_TIME_MESSAGE(TLF("Loading % from '%'", mmlWhat, *fileIt));
if (!XMLSubSys::runParser(myXMLHandler, *fileIt, isNet)) {
WRITE_MESSAGEF(TL("Loading of % failed."), mmlWhat);
return false;
}
PROGRESS_TIME_MESSAGE(before);
}
return true;
}
SUMORouteLoaderControl*
NLBuilder::buildRouteLoaderControl(const OptionsCont& oc) {
SUMORouteLoaderControl* loaders = new SUMORouteLoaderControl(string2time(oc.getString("route-steps")));
if (oc.isSet("route-files") && string2time(oc.getString("route-steps")) > 0) {
std::vector<std::string> files = oc.getStringVector("route-files");
for (std::vector<std::string>::const_iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
if (!FileHelpers::isReadable(*fileIt)) {
throw ProcessError(TLF("The route file '%' is not accessible.", *fileIt));
}
}
for (std::vector<std::string>::const_iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
loaders->add(new SUMORouteLoader(new MSRouteHandler(*fileIt, false)));
}
}
return loaders;
}
void
NLBuilder::buildDefaultMeanData(const std::string& optionName, const std::string& id, bool useLanes) {
if (OptionsCont::getOptions().isSet(optionName)) {
if (useLanes && MSGlobals::gUseMesoSim && !OptionsCont::getOptions().getBool("meso-lane-queue")) {
WRITE_WARNING(TL("LaneData requested for mesoscopic simulation but --meso-lane-queue is not active. Falling back to edgeData."));
useLanes = false;
}
try {
SUMOTime begin = string2time(OptionsCont::getOptions().getString("begin"));
myDetectorBuilder.createEdgeLaneMeanData(id, -1, begin, -1, "traffic", useLanes, false, false,
false, false, false, 100000, 0, SUMO_const_haltingSpeed, "", "", std::vector<MSEdge*>(), false,
OptionsCont::getOptions().getString(optionName));
} catch (InvalidArgument& e) {
WRITE_ERROR(e.what());
} catch (IOError& e) {
WRITE_ERROR(e.what());
}
}
}