#include <config.h>
#include <string>
#include <iostream>
#include <microsim/MSNet.h>
#include <microsim/MSLane.h>
#include <microsim/MSEdge.h>
#include <microsim/output/MSInductLoop.h>
#include <microsim/output/MSE2Collector.h>
#include <microsim/output/MSVTypeProbe.h>
#include <microsim/output/MSRouteProbe.h>
#include <microsim/output/MSMeanData_Net.h>
#include <microsim/output/MSMeanData_Emissions.h>
#include <microsim/output/MSMeanData_Harmonoise.h>
#include <microsim/output/MSMeanData_Amitran.h>
#include <microsim/output/MSInstantInductLoop.h>
#include <microsim/MSGlobals.h>
#include <microsim/actions/Command_SaveTLCoupledDet.h>
#include <microsim/actions/Command_SaveTLCoupledLaneDet.h>
#include <utils/common/UtilExceptions.h>
#include <utils/common/FileHelpers.h>
#include <utils/common/StringUtils.h>
#include <utils/common/StringTokenizer.h>
#include <utils/common/StringUtils.h>
#include "NLDetectorBuilder.h"
#include <microsim/output/MSDetectorControl.h>
#include <mesosim/MEInductLoop.h>
#include <mesosim/MELoop.h>
#include <mesosim/MESegment.h>
NLDetectorBuilder::E3DetectorDefinition::E3DetectorDefinition(const std::string& id,
const std::string& device, double haltingSpeedThreshold,
SUMOTime haltingTimeThreshold, SUMOTime splInterval,
const std::string name, const std::string& vTypes,
const std::string& nextEdges,
int detectPersons, bool openEntry, bool expectArrival) :
myID(id), myDevice(device),
myHaltingSpeedThreshold(haltingSpeedThreshold),
myHaltingTimeThreshold(haltingTimeThreshold),
mySampleInterval(splInterval),
myName(name),
myVehicleTypes(vTypes),
myNextEdges(nextEdges),
myDetectPersons(detectPersons),
myOpenEntry(openEntry),
myExpectArrival(expectArrival) {
}
NLDetectorBuilder::E3DetectorDefinition::~E3DetectorDefinition() {}
NLDetectorBuilder::NLDetectorBuilder(MSNet& net)
: myNet(net), myE3Definition(nullptr) {}
NLDetectorBuilder::~NLDetectorBuilder() {
delete myE3Definition;
}
Parameterised*
NLDetectorBuilder::buildInductLoop(const std::string& id,
const std::string& lane, double pos, double length, SUMOTime splInterval,
const std::string& device, bool friendlyPos,
const std::string name,
const std::string& vTypes,
const std::string& nextEdges,
int detectPersons) {
checkSampleInterval(splInterval, SUMO_TAG_E1DETECTOR, id);
MSLane* clane = getLaneChecking(lane, SUMO_TAG_E1DETECTOR, id);
pos = getPositionChecking(pos, clane, friendlyPos, SUMO_TAG_E1DETECTOR, id);
if (length < 0) {
throw InvalidArgument("The length of " + toString(SUMO_TAG_E1DETECTOR) + " '" + id + "' cannot be negative");
} else if (length > 0 && pos + length > clane->getLength()) {
if (friendlyPos) {
length = MIN2(length, clane->getLength());
pos = clane->getLength() - length;
} else {
throw InvalidArgument("The length of " + toString(SUMO_TAG_E1DETECTOR) + " '" + id + "' puts it beyond the lane's '" + clane->getID() + "' end.");
}
}
MSDetectorFileOutput* loop = createInductLoop(id, clane, pos, length, name, vTypes, nextEdges, detectPersons, true);
myNet.getDetectorControl().add(SUMO_TAG_INDUCTION_LOOP, loop, device, splInterval);
return loop;
}
Parameterised*
NLDetectorBuilder::buildInstantInductLoop(const std::string& id,
const std::string& lane, double pos,
const std::string& device, bool friendlyPos,
const std::string name,
const std::string& vTypes,
const std::string& nextEdges) {
MSLane* clane = getLaneChecking(lane, SUMO_TAG_INSTANT_INDUCTION_LOOP, id);
pos = getPositionChecking(pos, clane, friendlyPos, SUMO_TAG_INSTANT_INDUCTION_LOOP, id);
MSDetectorFileOutput* loop = createInstantInductLoop(id, clane, pos, device, name, vTypes, nextEdges);
myNet.getDetectorControl().add(SUMO_TAG_INSTANT_INDUCTION_LOOP, loop);
return loop;
}
Parameterised*
NLDetectorBuilder::buildE2Detector(const std::string& id, MSLane* lane, double pos, double endPos, double length,
const std::string& device, SUMOTime frequency,
SUMOTime haltingTimeThreshold, double haltingSpeedThreshold, double jamDistThreshold,
const std::string name, const std::string& vTypes,
const std::string& nextEdges,
int detectPersons, bool friendlyPos, bool showDetector,
MSTLLogicControl::TLSLogicVariants* tlls, MSLane* toLane) {
bool tlsGiven = tlls != nullptr;
bool toLaneGiven = toLane != nullptr;
bool posGiven = pos != std::numeric_limits<double>::max();
bool endPosGiven = endPos != std::numeric_limits<double>::max();
assert(posGiven || endPosGiven);
if (posGiven) {
if (pos >= lane->getLength() || (pos < 0 && -pos > lane->getLength())) {
std::stringstream ss;
ss << "The given position (=" << pos << ") for detector '" << id
<< "' does not lie on the given lane '" << lane->getID()
<< "' with length " << lane->getLength();
if (friendlyPos) {
double newPos = pos > 0 ? lane->getLength() - POSITION_EPS : 0.;
ss << " (adjusting to new position " << newPos;
WRITE_WARNING(ss.str());
pos = newPos;
} else {
ss << " (0 <= pos < lane->getLength() is required)";
throw InvalidArgument(ss.str());
}
}
}
if (endPosGiven) {
if (endPos > lane->getLength() || (endPos <= 0 && -endPos >= lane->getLength())) {
std::stringstream ss;
ss << "The given end position (=" << endPos << ") for detector '" << id
<< "' does not lie on the given lane '" << lane->getID()
<< "' with length " << lane->getLength();
if (friendlyPos) {
double newEndPos = endPos > 0 ? lane->getLength() : POSITION_EPS;
ss << " (adjusting to new position " << newEndPos;
WRITE_WARNING(ss.str());
pos = newEndPos;
} else {
ss << " (0 <= pos < lane->getLength() is required)";
throw InvalidArgument(ss.str());
}
}
}
MSE2Collector* det = nullptr;
if (tlsGiven) {
det = createE2Detector(id, DU_USER_DEFINED, lane, pos, endPos, length, haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold, name, vTypes, nextEdges, detectPersons, showDetector);
myNet.getDetectorControl().add(SUMO_TAG_LANE_AREA_DETECTOR, det);
if (toLaneGiven) {
const MSLane* const lastLane = det->getLastLane();
const MSLink* const link = lastLane->getLinkTo(toLane);
if (link == nullptr) {
throw InvalidArgument(
"The detector '" + id + "' cannot be build as no connection between lanes '"
+ lastLane->getID() + "' and '" + toLane->getID() + "' exists.");
}
new Command_SaveTLCoupledLaneDet(*tlls, det, myNet.getCurrentTimeStep(), OutputDevice::getDevice(device), link);
} else {
new Command_SaveTLCoupledDet(*tlls, det, myNet.getCurrentTimeStep(), OutputDevice::getDevice(device));
}
} else {
checkSampleInterval(frequency, SUMO_TAG_E2DETECTOR, id);
det = createE2Detector(id, DU_USER_DEFINED, lane, pos, endPos, length, haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold, name, vTypes, nextEdges, detectPersons, showDetector);
myNet.getDetectorControl().add(SUMO_TAG_LANE_AREA_DETECTOR, det, device, frequency);
}
return det;
}
Parameterised*
NLDetectorBuilder::buildE2Detector(const std::string& id, std::vector<MSLane*> lanes, double pos, double endPos,
const std::string& device, SUMOTime frequency,
SUMOTime haltingTimeThreshold, double haltingSpeedThreshold, double jamDistThreshold,
const std::string name, const std::string& vTypes,
const std::string& nextEdges,
int detectPersons, bool friendlyPos, bool showDetector,
MSTLLogicControl::TLSLogicVariants* tlls, MSLane* toLane) {
bool tlsGiven = tlls != nullptr;
bool toLaneGiven = toLane != nullptr;
assert(pos != std::numeric_limits<double>::max());
assert(endPos != std::numeric_limits<double>::max());
assert(lanes.size() != 0);
const MSLane* const firstLane = lanes[0];
const MSLane* const lastLane = lanes.back();
if (pos >= firstLane->getLength() || (pos < 0 && -pos > firstLane->getLength())) {
std::stringstream ss;
ss << "The given position (=" << pos << ") for detector '" << id
<< "' does not lie on the given lane '" << firstLane->getID()
<< "' with length " << firstLane->getLength();
if (friendlyPos) {
double newPos = pos > 0 ? firstLane->getLength() - POSITION_EPS : 0.;
ss << " (adjusting to new position " << newPos;
WRITE_WARNING(ss.str());
pos = newPos;
} else {
ss << " (0 <= pos < lane->getLength() is required)";
throw InvalidArgument(ss.str());
}
}
if (endPos > lastLane->getLength() || (endPos <= 0 && -endPos >= lastLane->getLength())) {
std::stringstream ss;
ss << "The given end position (=" << endPos << ") for detector '" << id
<< "' does not lie on the given lane '" << lastLane->getID()
<< "' with length " << lastLane->getLength();
if (friendlyPos) {
double newEndPos = endPos > 0 ? lastLane->getLength() : POSITION_EPS;
ss << " (adjusting to new position " << newEndPos;
WRITE_WARNING(ss.str());
pos = newEndPos;
} else {
ss << " (0 <= pos < lane->getLength() is required)";
throw InvalidArgument(ss.str());
}
}
MSE2Collector* det = nullptr;
if (tlsGiven) {
det = createE2Detector(id, DU_USER_DEFINED, lanes, pos, endPos, haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold, name, vTypes, nextEdges, detectPersons, showDetector);
myNet.getDetectorControl().add(SUMO_TAG_LANE_AREA_DETECTOR, det);
if (toLaneGiven) {
const MSLane* const lastDetLane = det->getLastLane();
const MSLink* const link = lastDetLane->getLinkTo(toLane);
if (link == nullptr) {
throw InvalidArgument(
"The detector '" + id + "' cannot be build as no connection between lanes '"
+ lastDetLane->getID() + "' and '" + toLane->getID() + "' exists.");
}
new Command_SaveTLCoupledLaneDet(*tlls, det, myNet.getCurrentTimeStep(), OutputDevice::getDevice(device), link);
} else {
new Command_SaveTLCoupledDet(*tlls, det, myNet.getCurrentTimeStep(), OutputDevice::getDevice(device));
}
} else {
checkSampleInterval(frequency, SUMO_TAG_E2DETECTOR, id);
det = createE2Detector(id, DU_USER_DEFINED, lanes, pos, endPos, haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold, name, vTypes, nextEdges, detectPersons, showDetector);
myNet.getDetectorControl().add(SUMO_TAG_LANE_AREA_DETECTOR, det, device, frequency);
}
return det;
}
Parameterised*
NLDetectorBuilder::beginE3Detector(const std::string& id,
const std::string& device, SUMOTime splInterval,
double haltingSpeedThreshold,
SUMOTime haltingTimeThreshold,
const std::string name,
const std::string& vTypes,
const std::string& nextEdges,
int detectPersons, bool openEntry, bool expectArrival) {
checkSampleInterval(splInterval, SUMO_TAG_E3DETECTOR, id);
myE3Definition = new E3DetectorDefinition(id, device, haltingSpeedThreshold, haltingTimeThreshold, splInterval, name, vTypes, nextEdges, detectPersons, openEntry, expectArrival);
return myE3Definition;
}
void
NLDetectorBuilder::addE3Entry(const std::string& lane,
double pos, bool friendlyPos) {
if (myE3Definition == nullptr) {
return;
}
MSLane* clane = getLaneChecking(lane, SUMO_TAG_E3DETECTOR, myE3Definition->myID);
pos = getPositionChecking(pos, clane, friendlyPos, SUMO_TAG_DET_ENTRY, myE3Definition->myID);
myE3Definition->myEntries.push_back(MSCrossSection(clane, pos));
}
void
NLDetectorBuilder::addE3Exit(const std::string& lane,
double pos, bool friendlyPos) {
if (myE3Definition == nullptr) {
return;
}
MSLane* clane = getLaneChecking(lane, SUMO_TAG_E3DETECTOR, myE3Definition->myID);
pos = getPositionChecking(pos, clane, friendlyPos, SUMO_TAG_DET_EXIT, myE3Definition->myID);
myE3Definition->myExits.push_back(MSCrossSection(clane, pos));
}
std::string
NLDetectorBuilder::getCurrentE3ID() const {
if (myE3Definition == nullptr) {
return "<unknown>";
}
return myE3Definition->myID;
}
void
NLDetectorBuilder::endE3Detector() {
if (myE3Definition == nullptr) {
return;
}
if (myE3Definition->myEntries.size() > 0 || myE3Definition->myExits.size() > 0) {
MSDetectorFileOutput* det = createE3Detector(myE3Definition->myID,
myE3Definition->myEntries, myE3Definition->myExits,
myE3Definition->myHaltingSpeedThreshold, myE3Definition->myHaltingTimeThreshold,
myE3Definition->myName,
myE3Definition->myVehicleTypes,
myE3Definition->myNextEdges,
myE3Definition->myDetectPersons,
myE3Definition->myOpenEntry,
myE3Definition->myExpectArrival);
det->updateParameters(myE3Definition->getParametersMap());
myNet.getDetectorControl().add(SUMO_TAG_ENTRY_EXIT_DETECTOR, det, myE3Definition->myDevice, myE3Definition->mySampleInterval);
} else {
WRITE_WARNING(toString(SUMO_TAG_E3DETECTOR) + " with id = '" + myE3Definition->myID + "' will not be created because is empty (no " + toString(SUMO_TAG_DET_ENTRY) + " or " + toString(SUMO_TAG_DET_EXIT) + " was defined)")
}
delete myE3Definition;
myE3Definition = nullptr;
}
void
NLDetectorBuilder::buildVTypeProbe(const std::string& id,
const std::string& vtype, SUMOTime frequency,
const std::string& device) {
checkSampleInterval(frequency, SUMO_TAG_VTYPEPROBE, id);
new MSVTypeProbe(id, vtype, OutputDevice::getDevice(device), frequency);
}
void
NLDetectorBuilder::buildRouteProbe(const std::string& id, const std::string& edge,
SUMOTime frequency, SUMOTime begin,
const std::string& device,
const std::string& vTypes) {
checkSampleInterval(frequency, SUMO_TAG_ROUTEPROBE, id);
MSEdge* e = getEdgeChecking(edge, SUMO_TAG_ROUTEPROBE, id);
MSRouteProbe* probe = new MSRouteProbe(id, e, id + "_" + toString(begin), id + "_" + toString(begin - frequency), vTypes);
myNet.getDetectorControl().add(SUMO_TAG_ROUTEPROBE, probe, device, frequency, begin);
}
MSDetectorFileOutput*
NLDetectorBuilder::createInductLoop(const std::string& id,
MSLane* lane, double pos,
double length,
const std::string name,
const std::string& vTypes,
const std::string& nextEdges,
int detectPersons,
bool ) {
if (MSGlobals::gUseMesoSim) {
return new MEInductLoop(id, MSGlobals::gMesoNet->getSegmentForEdge(lane->getEdge(), pos), pos, name, vTypes, nextEdges, detectPersons);
}
return new MSInductLoop(id, lane, pos, length, name, vTypes, nextEdges, detectPersons, false);
}
MSDetectorFileOutput*
NLDetectorBuilder::createInstantInductLoop(const std::string& id,
MSLane* lane, double pos, const std::string& od,
const std::string name, const std::string& vTypes,
const std::string& nextEdges) {
return new MSInstantInductLoop(id, OutputDevice::getDevice(od), lane, pos, name, vTypes, nextEdges);
}
MSE2Collector*
NLDetectorBuilder::createE2Detector(const std::string& id,
DetectorUsage usage, MSLane* lane, double pos, double endPos, double length,
SUMOTime haltingTimeThreshold, double haltingSpeedThreshold, double jamDistThreshold,
const std::string name, const std::string& vTypes,
const std::string& nextEdges,
int detectPersons, bool ) {
return new MSE2Collector(id, usage, lane, pos, endPos, length, haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold, name, vTypes, nextEdges, detectPersons);
}
MSE2Collector*
NLDetectorBuilder::createE2Detector(const std::string& id,
DetectorUsage usage, std::vector<MSLane*> lanes, double pos, double endPos,
SUMOTime haltingTimeThreshold, double haltingSpeedThreshold, double jamDistThreshold,
const std::string name, const std::string& vTypes,
const std::string& nextEdges,
int detectPersons, bool ) {
return new MSE2Collector(id, usage, lanes, pos, endPos, haltingTimeThreshold, haltingSpeedThreshold, jamDistThreshold, name, vTypes, nextEdges, detectPersons);
}
MSDetectorFileOutput*
NLDetectorBuilder::createE3Detector(const std::string& id,
const CrossSectionVector& entries,
const CrossSectionVector& exits,
double haltingSpeedThreshold,
SUMOTime haltingTimeThreshold,
const std::string name, const std::string& vTypes,
const std::string& nextEdges,
int detectPersons,
bool openEntry,
bool expectArrival) {
return new MSE3Collector(id, entries, exits, haltingSpeedThreshold, haltingTimeThreshold, name, vTypes, nextEdges, detectPersons, openEntry, expectArrival);
}
double
NLDetectorBuilder::getPositionChecking(double pos, MSLane* lane, bool friendlyPos,
SumoXMLTag tag,
const std::string& detid) {
if (pos < 0) {
pos += lane->getLength();
}
if (pos > lane->getLength()) {
if (friendlyPos) {
pos = lane->getLength();
} else {
throw InvalidArgument("The position of " + toString(tag) + " '" + detid + "' lies beyond the lane's '" + lane->getID() + "' end.");
}
}
if (pos < 0) {
if (friendlyPos) {
pos = 0.;
} else {
throw InvalidArgument("The position of " + toString(tag) + " '" + detid + "' lies before the lane's '" + lane->getID() + "' begin.");
}
}
return pos;
}
void
NLDetectorBuilder::createEdgeLaneMeanData(const std::string& id, SUMOTime frequency,
SUMOTime begin, SUMOTime end, const std::string& type,
const bool useLanes, const bool withEmpty, const bool printDefaults,
const bool withInternal, const bool trackVehicles, const int detectPersons,
const double maxTravelTime, const double minSamples,
const double haltSpeed, const std::string& vTypes,
const std::string& writeAttributes,
std::vector<MSEdge*> edges,
bool aggregate,
const std::string& device) {
if (begin < 0) {
throw InvalidArgument("Negative begin time for meandata dump '" + id + "'.");
}
if (end < 0) {
end = SUMOTime_MAX;
}
if (end <= begin) {
throw InvalidArgument("End before or at begin for meandata dump '" + id + "'.");
}
checkStepLengthMultiple(begin, " for meandata dump '" + id + "'");
MSMeanData* det = nullptr;
if (type == "" || type == "performance" || type == "traffic") {
det = new MSMeanData_Net(id, begin, end, useLanes, withEmpty,
printDefaults, withInternal, trackVehicles, detectPersons, maxTravelTime, minSamples, haltSpeed, vTypes, writeAttributes, edges, aggregate);
} else if (type == "emissions" || type == "hbefa") {
if (type == "hbefa") {
WRITE_WARNING(TL("The netstate type 'hbefa' is deprecated. Please use the type 'emissions' instead."));
}
det = new MSMeanData_Emissions(id, begin, end, useLanes, withEmpty,
printDefaults, withInternal, trackVehicles, maxTravelTime, minSamples, vTypes, writeAttributes, edges, aggregate);
} else if (type == "harmonoise") {
det = new MSMeanData_Harmonoise(id, begin, end, useLanes, withEmpty,
printDefaults, withInternal, trackVehicles, maxTravelTime, minSamples, vTypes, writeAttributes, edges, aggregate);
} else if (type == "amitran") {
det = new MSMeanData_Amitran(id, begin, end, useLanes, withEmpty,
printDefaults, withInternal, trackVehicles, detectPersons, maxTravelTime, minSamples, haltSpeed, vTypes, writeAttributes, edges, aggregate);
} else {
throw InvalidArgument("Invalid type '" + type + "' for meandata dump '" + id + "'.");
}
if (det != nullptr) {
if (frequency < 0) {
frequency = end - begin;
} else {
checkStepLengthMultiple(frequency, " for meandata dump '" + id + "'");
}
MSNet::getInstance()->getDetectorControl().add(det, device, frequency, begin);
}
}
MSEdge*
NLDetectorBuilder::getEdgeChecking(const std::string& edgeID, SumoXMLTag type,
const std::string& detid) {
MSEdge* edge = MSEdge::dictionary(edgeID);
if (edge == nullptr) {
throw InvalidArgument("The lane with the id '" + edgeID + "' is not known (while building " + toString(type) + " '" + detid + "').");
}
return edge;
}
MSLane*
NLDetectorBuilder::getLaneChecking(const std::string& laneID, SumoXMLTag type,
const std::string& detid) {
MSLane* lane = MSLane::dictionary(laneID);
if (lane == nullptr) {
throw InvalidArgument("The lane with the id '" + laneID + "' is not known (while building " + toString(type) + " '" + detid + "').");
}
return lane;
}
void
NLDetectorBuilder::checkSampleInterval(SUMOTime splInterval, SumoXMLTag type, const std::string& id) {
if (splInterval < 0) {
throw InvalidArgument("Negative sampling frequency (in " + toString(type) + " '" + id + "').");
}
if (splInterval == 0) {
throw InvalidArgument("Sampling frequency must not be zero (in " + toString(type) + " '" + id + "').");
}
checkStepLengthMultiple(splInterval, " (in " + toString(type) + " '" + id + "')");
}