#include <config.h>
#include <string>
#include <algorithm>
#include <cmath>
#include <microsim/MSGlobals.h>
#include <microsim/MSNet.h>
#include <microsim/MSEdge.h>
#include <microsim/MSEventControl.h>
#include <microsim/MSVehicleControl.h>
#include <microsim/output/MSRouteProbe.h>
#include <utils/xml/SUMOXMLDefinitions.h>
#include <utils/common/MsgHandler.h>
#include <utils/common/ToString.h>
#include <utils/common/UtilExceptions.h>
#include <utils/common/StringTokenizer.h>
#include <utils/xml/XMLSubSys.h>
#include <utils/common/StringUtils.h>
#include <utils/options/OptionsCont.h>
#include <utils/vehicle/SUMOVehicleParserHelper.h>
#include <utils/distribution/RandomDistributor.h>
#include <utils/vehicle/SUMOVehicleParameter.h>
#include "MELoop.h"
#include "MESegment.h"
#include "MEVehicle.h"
#include "METriggeredCalibrator.h"
METriggeredCalibrator::METriggeredCalibrator(const std::string& id,
MSEdge* const edge, const double pos,
const std::string& aXMLFilename,
const std::string& outputFilename,
const SUMOTime freq, const double length,
const MSRouteProbe* probe,
const double invalidJamThreshold,
const std::string& vTypes) :
MSCalibrator(id, edge, nullptr, nullptr, pos, aXMLFilename, outputFilename, freq, length, probe, invalidJamThreshold, vTypes, false, false),
mySegment(edge == nullptr ? nullptr : MSGlobals::gMesoNet->getSegmentForEdge(*edge, pos)) {
myEdgeMeanData.setDescription("meandata_calibrator_" + getID());
if (mySegment != nullptr) {
mySegment->addDetector(&myEdgeMeanData);
}
}
METriggeredCalibrator::~METriggeredCalibrator() {
if (myCurrentStateInterval != myIntervals.end()) {
intervalEnd();
myCurrentStateInterval = myIntervals.end();
}
}
bool
METriggeredCalibrator::tryEmit(MESegment* s, MEVehicle* vehicle) {
if (s->initialise(vehicle, vehicle->getParameter().depart)) {
if (!MSNet::getInstance()->getVehicleControl().addVehicle(vehicle->getID(), vehicle)) {
throw ProcessError(TLF("Emission of vehicle '%' in calibrator '%' failed!", vehicle->getID(), getID()));
}
return true;
}
return false;
}
SUMOTime
METriggeredCalibrator::execute(SUMOTime currentTime) {
mySegment->prepareDetectorForWriting(myEdgeMeanData);
if (isCurrentStateActive(currentTime)) {
myAmActive = true;
} else {
myAmActive = false;
myEdgeMeanData.reset();
if (!mySpeedIsDefault) {
const double jamThresh = OptionsCont::getOptions().getFloat("meso-jam-threshold");
myEdge->setMaxSpeed(myDefaultSpeed, jamThresh);
mySpeedIsDefault = true;
}
if (myCurrentStateInterval == myIntervals.end()) {
return TIME2STEPS(86400);
}
return myFrequency;
}
const bool calibrateFlow = myCurrentStateInterval->q >= 0;
const bool calibrateSpeed = myCurrentStateInterval->v >= 0;
if (!myDidSpeedAdaption && calibrateSpeed && myCurrentStateInterval->v != mySegment->getEdge().getSpeedLimit()) {
myEdge->setMaxSpeed(myCurrentStateInterval->v);
mySpeedIsDefault = false;
myDidSpeedAdaption = true;
}
bool hadInvalidJam = false;
while ((calibrateFlow || calibrateSpeed) && invalidJam()) {
hadInvalidJam = true;
if (!myHaveWarnedAboutClearingJam) {
WRITE_WARNINGF(TL("Clearing jam at calibrator '%' at time=%."), getID(), time2string(currentTime));
}
if (mySegment->vaporizeAnyCar(currentTime, this)) {
myClearedInJam++;
} else {
if (!myHaveWarnedAboutClearingJam) {
WRITE_WARNINGF(TL("Could not clear jam at calibrator '%' at time=%."), getID(), time2string(currentTime));
}
break;
}
myHaveWarnedAboutClearingJam = true;
}
if (calibrateFlow) {
const double totalHourFraction = STEPS2TIME(myCurrentStateInterval->end - myCurrentStateInterval->begin) / (double) 3600.;
const int totalWishedNum = (int)std::floor(myCurrentStateInterval->q * totalHourFraction + 0.5);
int adaptedNum = passed() + myClearedInJam;
if (!hadInvalidJam) {
const double hourFraction = STEPS2TIME(currentTime - myCurrentStateInterval->begin + DELTA_T) / (double) 3600.;
const int wishedNum = (int)std::floor(myCurrentStateInterval->q * hourFraction + 0.5);
const int relaxedInsertion = (int)std::floor(STEPS2TIME(myCurrentStateInterval->end - currentTime) / 3);
const int insertionSlack = MAX2(0, adaptedNum + relaxedInsertion - totalWishedNum);
MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
while (wishedNum > adaptedNum + insertionSlack && remainingVehicleCapacity() > maximumInflow()) {
SUMOVehicleParameter* pars = myCurrentStateInterval->vehicleParameter;
ConstMSRoutePtr route = myProbe != nullptr ? myProbe->sampleRoute() : nullptr;
if (route == nullptr) {
route = MSRoute::dictionary(pars->routeid);
}
if (route == nullptr) {
WRITE_WARNINGF(TL("No valid routes in calibrator '%'."), getID());
break;
}
if (!route->contains(myEdge)) {
WRITE_WARNINGF(TL("Route '%' in calibrator '%' does not contain edge '%'."), route->getID(), getID(), myEdge->getID());
break;
}
MSVehicleType* vtype = vc.getVType(pars->vtypeid);
assert(route != 0 && vtype != 0);
const SUMOTime depart = mySegment->getNextInsertionTime(currentTime);
SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
newPars->id = getNewVehicleID();
newPars->depart = depart;
newPars->routeid = route->getID();
MEVehicle* vehicle;
try {
vehicle = static_cast<MEVehicle*>(vc.buildVehicle(newPars, route, vtype, false, MSVehicleControl::VehicleDefinitionSource::TRIGGER));
std::string msg;
if (!vehicle->hasValidRouteStart(msg)) {
throw ProcessError(msg);
}
} catch (const ProcessError& e) {
if (!MSGlobals::gCheckRoutes) {
WRITE_WARNING(e.what());
vehicle = nullptr;
break;
} else {
throw;
}
}
const bool duplicate = vc.getVehicle(newPars->id) != nullptr;
if (duplicate) {
vc.deleteVehicle(vehicle, true);
continue;
}
vehicle->setSegment(mySegment);
vehicle->setEventTime(currentTime);
const MSEdge* myedge = &mySegment->getEdge();
bool atDest = false;
while (vehicle->getEdge() != myedge) {
atDest = vehicle->moveRoutePointer();
}
if (atDest || !tryEmit(mySegment, vehicle)) {
vc.deleteVehicle(vehicle, true);
break;
}
myInserted++;
adaptedNum++;
}
}
while (totalWishedNum < adaptedNum) {
if (!mySegment->vaporizeAnyCar(currentTime, this)) {
break;
}
myRemoved++;
adaptedNum--;
}
}
if (myCurrentStateInterval->end <= currentTime + myFrequency) {
intervalEnd();
}
if (invalidJam()) {
WRITE_WARNINGF("DEBUG: Could not clear jam at calibrator '%' at time=%.", getID(), time2string(currentTime));
}
return myFrequency;
}
bool
METriggeredCalibrator::invalidJam() const {
if (mySegment->getBruttoOccupancy() == 0.) {
return false;
}
const bool toSlow = mySegment->getMeanSpeed() < myInvalidJamThreshold * mySegment->getEdge().getSpeedLimit();
return toSlow && remainingVehicleCapacity() < maximumInflow();
}
int
METriggeredCalibrator::remainingVehicleCapacity() const {
const SUMOVehicleParameter* pars = myCurrentStateInterval->vehicleParameter;
const MSVehicleType* vtype = MSNet::getInstance()->getVehicleControl().getVType(pars->vtypeid);
return mySegment->remainingVehicleCapacity(vtype->getLengthWithGap());
}
void
METriggeredCalibrator::reset() {
myEdgeMeanData.reset();
}