#include <config.h>
#include <iostream>
#include <algorithm>
#include <cassert>
#include <iterator>
#include <utils/router/IntermodalRouter.h>
#include <microsim/devices/MSDevice_Routing.h>
#include <microsim/devices/MSRoutingEngine.h>
#include "MSGlobals.h"
#include "MSVehicle.h"
#include "MSVehicleControl.h"
#include "MSLane.h"
#include "MSEdge.h"
#include "MSNet.h"
#include "MSRouteHandler.h"
#include "MSInsertionControl.h"
MSInsertionControl::MSInsertionControl(MSVehicleControl& vc,
SUMOTime maxDepartDelay,
bool eagerInsertionCheck,
int maxVehicleNumber,
SUMOTime randomDepartOffset) :
myVehicleControl(vc),
myMaxDepartDelay(maxDepartDelay),
myEagerInsertionCheck(eagerInsertionCheck),
myMaxVehicleNumber(maxVehicleNumber),
myPendingEmitsUpdateTime(SUMOTime_MIN),
myFlowRNG("flow") {
myMaxRandomDepartOffset = randomDepartOffset;
RandHelper::initRandGlobal(&myFlowRNG);
}
MSInsertionControl::~MSInsertionControl() {
for (const Flow& f : myFlows) {
delete (f.pars);
}
}
void
MSInsertionControl::add(SUMOVehicle* veh) {
myAllVeh.add(veh);
}
bool
MSInsertionControl::addFlow(SUMOVehicleParameter* const pars, int index) {
if (myFlowIDs.count(pars->id) > 0) {
return false;
}
const bool loadingFromState = index >= 0;
Flow flow{pars, loadingFromState ? index : 0, initScale(pars->vtypeid)};
if (!loadingFromState && pars->repetitionProbability < 0 && pars->repetitionOffset < 0) {
flow.pars->incrementFlow(flow.scale, &myFlowRNG);
flow.pars->repetitionsDone--;
}
myFlows.emplace_back(flow);
myFlowIDs.insert(std::make_pair(pars->id, flow.index));
return true;
}
double
MSInsertionControl::initScale(const std::string vtypeid) {
MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
if (vc.hasVTypeDistribution(vtypeid)) {
double result = -1;
const RandomDistributor<MSVehicleType*>* dist = vc.getVTypeDistribution(vtypeid);
for (const MSVehicleType* t : dist->getVals()) {
if (result == -1) {
result = t->getParameter().scale;
} else if (result != t->getParameter().scale) {
return -1;
}
}
return result;
} else {
return vc.getVType(vtypeid, nullptr, true)->getParameter().scale;
}
}
void
MSInsertionControl::updateScale(const std::string vtypeid) {
for (Flow& f : myFlows) {
if (f.pars->vtypeid == vtypeid) {
f.scale = initScale(vtypeid);
}
}
}
int
MSInsertionControl::emitVehicles(SUMOTime time) {
const bool havePreChecked = MSRoutingEngine::isEnabled();
if (myPendingEmits.empty() || (havePreChecked && myEmitCandidates.empty())) {
return 0;
}
int numEmitted = 0;
MSVehicleContainer::VehicleVector refusedEmits;
MSVehicleContainer::VehicleVector::const_iterator veh;
for (veh = myPendingEmits.begin(); veh != myPendingEmits.end(); veh++) {
if (havePreChecked && (myEmitCandidates.count(*veh) == 0)) {
refusedEmits.push_back(*veh);
} else {
numEmitted += tryInsert(time, *veh, refusedEmits);
}
}
myEmitCandidates.clear();
myPendingEmits = refusedEmits;
return numEmitted;
}
int
MSInsertionControl::tryInsert(SUMOTime time, SUMOVehicle* veh,
MSVehicleContainer::VehicleVector& refusedEmits) {
assert(veh->getParameter().depart <= time);
const MSEdge& edge = *veh->getEdge();
if (veh->isOnRoad()) {
return 1;
}
if ((myMaxVehicleNumber < 0 || (int)MSNet::getInstance()->getVehicleControl().getRunningVehicleNo() < myMaxVehicleNumber)
&& edge.insertVehicle(*veh, time, false, myEagerInsertionCheck || veh->getParameter().departProcedure == DepartDefinition::SPLIT)) {
return 1;
}
if (myMaxDepartDelay >= 0 && time - veh->getParameter().depart > myMaxDepartDelay) {
myVehicleControl.deleteVehicle(veh, true);
} else if (edge.isVaporizing()) {
myVehicleControl.deleteVehicle(veh, true);
} else if (myAbortedEmits.count(veh) > 0) {
myAbortedEmits.erase(veh);
myVehicleControl.deleteVehicle(veh, true);
} else if ((veh->getRouteValidity(false) & (
MSBaseVehicle::ROUTE_START_INVALID_LANE
| MSBaseVehicle::ROUTE_START_INVALID_PERMISSIONS)) != 0) {
myVehicleControl.deleteVehicle(veh, true);
} else {
refusedEmits.push_back(veh);
}
edge.setLastFailedInsertionTime(time);
return 0;
}
void
MSInsertionControl::checkCandidates(SUMOTime time, const bool preCheck) {
while (myAllVeh.anyWaitingBefore(time)) {
const MSVehicleContainer::VehicleVector& top = myAllVeh.top();
copy(top.begin(), top.end(), back_inserter(myPendingEmits));
myAllVeh.pop();
}
if (preCheck) {
MSVehicleContainer::VehicleVector::const_iterator veh;
for (veh = myPendingEmits.begin(); veh != myPendingEmits.end(); veh++) {
SUMOVehicle* const v = *veh;
const MSEdge* const edge = v->getEdge();
if (edge->insertVehicle(*v, time, true, myEagerInsertionCheck)) {
myEmitCandidates.insert(v);
} else {
MSDevice_Routing* dev = static_cast<MSDevice_Routing*>(v->getDevice(typeid(MSDevice_Routing)));
if (dev != nullptr) {
dev->skipRouting(time);
}
}
}
}
}
void
MSInsertionControl::determineCandidates(SUMOTime time) {
MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
for (std::vector<Flow>::iterator i = myFlows.begin(); i != myFlows.end();) {
MSVehicleType* vtype = nullptr;
SUMOVehicleParameter* pars = i->pars;
double typeScale = i->scale;
if (typeScale < 0) {
vtype = vehControl.getVType(pars->vtypeid, MSRouteHandler::getParsingRNG());
typeScale = vtype->getParameter().scale;
}
double scale = vehControl.getScale() * typeScale;
bool tryEmitByProb = pars->repetitionProbability > 0;
while (scale > 0 && ((pars->repetitionProbability < 0
&& pars->repetitionsDone < pars->repetitionNumber * scale
&& pars->depart + pars->repetitionTotalOffset <= time)
|| (tryEmitByProb
&& pars->depart <= time
&& pars->repetitionEnd > time
&& RandHelper::rand(&myFlowRNG) < (pars->repetitionProbability * TS))
)) {
tryEmitByProb = false;
SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
newPars->id = pars->id + "." + toString(i->index);
newPars->depart = pars->repetitionProbability > 0 ? time : pars->depart + pars->repetitionTotalOffset + computeRandomDepartOffset();
pars->incrementFlow(scale, &myFlowRNG);
myFlowIDs[pars->id] = i->index;
if (vehControl.getVehicle(newPars->id) == nullptr) {
ConstMSRoutePtr const route = MSRoute::dictionary(pars->routeid);
if (vtype == nullptr) {
vtype = vehControl.getVType(pars->vtypeid, MSRouteHandler::getParsingRNG());
}
SUMOVehicle* const vehicle = vehControl.buildVehicle(newPars, route, vtype, !MSGlobals::gCheckRoutes);
int quota = pars->repetitionProbability < 0 ? 1 : vehControl.getQuota(scale);
if (quota > 0) {
vehControl.addVehicle(newPars->id, vehicle);
if (pars->departProcedure == DepartDefinition::GIVEN || pars->departProcedure == DepartDefinition::BEGIN) {
add(vehicle);
}
i->index++;
while (--quota > 0) {
SUMOVehicleParameter* const quotaPars = new SUMOVehicleParameter(*pars);
quotaPars->id = pars->id + "." + toString(i->index);
quotaPars->depart = pars->repetitionProbability > 0 ? time :
pars->depart + pars->repetitionsDone * pars->repetitionTotalOffset + computeRandomDepartOffset();
SUMOVehicle* const quotaVehicle = vehControl.buildVehicle(quotaPars, route, vtype, !MSGlobals::gCheckRoutes);
vehControl.addVehicle(quotaPars->id, quotaVehicle);
if (pars->departProcedure == DepartDefinition::GIVEN || pars->departProcedure == DepartDefinition::BEGIN) {
add(quotaVehicle);
}
pars->repetitionsDone++;
i->index++;
}
} else {
vehControl.deleteVehicle(vehicle, true);
}
} else {
if (MSGlobals::gStateLoaded) {
break;
}
throw ProcessError(TLF("Another vehicle with the id '%' exists.", newPars->id));
}
vtype = nullptr;
}
if (time >= pars->repetitionEnd ||
(pars->repetitionNumber != std::numeric_limits<long long int>::max()
&& pars->repetitionsDone >= (long long int)(pars->repetitionNumber * scale + 0.5))) {
i = myFlows.erase(i);
MSRoute::checkDist(pars->routeid);
delete pars;
} else {
++i;
}
}
checkCandidates(time, MSRoutingEngine::isEnabled());
}
int
MSInsertionControl::getWaitingVehicleNo() const {
return (int)myPendingEmits.size();
}
int
MSInsertionControl::getPendingFlowCount() const {
return (int)myFlows.size();
}
void
MSInsertionControl::descheduleDeparture(const SUMOVehicle* veh) {
myAbortedEmits.insert(veh);
}
void
MSInsertionControl::retractDescheduleDeparture(const SUMOVehicle* veh) {
myAbortedEmits.erase(veh);
}
void
MSInsertionControl::alreadyDeparted(SUMOVehicle* veh) {
myPendingEmits.erase(std::remove(myPendingEmits.begin(), myPendingEmits.end(), veh), myPendingEmits.end());
myAllVeh.remove(veh);
}
void
MSInsertionControl::clearPendingVehicles(const std::string& route) {
MSVehicleContainer::VehicleVector::iterator veh;
for (veh = myPendingEmits.begin(); veh != myPendingEmits.end();) {
if ((*veh)->getRoute().getID() == route || route == "") {
myVehicleControl.deleteVehicle(*veh, true);
veh = myPendingEmits.erase(veh);
} else {
++veh;
}
}
}
int
MSInsertionControl::getPendingEmits(const MSLane* lane) {
if (MSNet::getInstance()->getCurrentTimeStep() != myPendingEmitsUpdateTime) {
myPendingEmitsForLane.clear();
for (const SUMOVehicle* const veh : myPendingEmits) {
const MSLane* const vlane = veh->getLane();
if (vlane != nullptr) {
myPendingEmitsForLane[vlane]++;
} else {
for (const MSLane* const l : veh->getEdge()->getLanes()) {
myPendingEmitsForLane[l]++;
}
}
}
myPendingEmitsUpdateTime = MSNet::getInstance()->getCurrentTimeStep();
}
return myPendingEmitsForLane[lane];
}
void
MSInsertionControl::adaptIntermodalRouter(MSTransportableRouter& router) const {
for (const Flow& f : myFlows) {
if (f.pars->line != "") {
ConstMSRoutePtr const route = MSRoute::dictionary(f.pars->routeid);
router.getNetwork()->addSchedule(*f.pars, route == nullptr ? nullptr : &route->getStops());
}
}
}
void
MSInsertionControl::saveState(OutputDevice& out) {
for (const Flow& flow : myFlows) {
flow.pars->write(out, OptionsCont::getOptions(), SUMO_TAG_FLOWSTATE,
flow.pars->vtypeid == DEFAULT_VTYPE_ID ? "" : flow.pars->vtypeid);
if (flow.pars->repetitionProbability <= 0) {
out.writeAttr(SUMO_ATTR_NEXT, STEPS2TIME(flow.pars->repetitionTotalOffset));
}
out.writeAttr(SUMO_ATTR_ROUTE, flow.pars->routeid);
out.writeAttr(SUMO_ATTR_DONE, flow.pars->repetitionsDone);
out.writeAttr(SUMO_ATTR_INDEX, flow.index);
if (flow.pars->wasSet(VEHPARS_FORCE_REROUTE)) {
out.writeAttr(SUMO_ATTR_REROUTE, true);
}
for (const SUMOVehicleParameter::Stop& stop : flow.pars->stops) {
stop.write(out);
}
out.closeTag();
}
}
void
MSInsertionControl::clearState() {
for (const Flow& f : myFlows) {
delete (f.pars);
}
myFlows.clear();
myFlowIDs.clear();
myAllVeh.clearState();
myPendingEmits.clear();
myEmitCandidates.clear();
myAbortedEmits.clear();
}
SUMOTime
MSInsertionControl::computeRandomDepartOffset() const {
if (myMaxRandomDepartOffset > 0) {
return DELTA_T * ((RandHelper::rand(myMaxRandomDepartOffset, MSRouteHandler::getParsingRNG()) + DELTA_T / 2) / DELTA_T);
}
return 0;
}
const SUMOVehicleParameter*
MSInsertionControl::getFlowPars(const std::string& id) const {
if (hasFlow(id)) {
for (const Flow& f : myFlows) {
if (f.pars->id == id) {
return f.pars;
}
}
}
return nullptr;
}
SUMOVehicle*
MSInsertionControl::getLastFlowVehicle(const std::string& id) const {
const auto it = myFlowIDs.find(id);
if (it != myFlowIDs.end()) {
const std::string vehID = id + "." + toString(it->second);
return MSNet::getInstance()->getVehicleControl().getVehicle(vehID);
}
return nullptr;
}
bool
MSInsertionControl::hasTaxiFlow() const {
SumoRNG tmp("tmp");
for (const Flow& flow : myFlows) {
if (flow.scale != 0 &&
(StringUtils::toBool(flow.pars->getParameter("has.taxi.device", "false"))
|| hasTaxiDeviceType(flow.pars->vtypeid, tmp))) {
return true;
}
}
return false;
}
bool
MSInsertionControl::hasTaxiDeviceType(const std::string& vtypeId, SumoRNG& rng) {
MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
const MSVehicleType* vtype = vehControl.getVType(vtypeId, &rng);
return StringUtils::toBool(vtype->getParameter().getParameter("has.taxi.device", "false"));
}