#pragma once
#include <config.h>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <assert.h>
#include <utils/common/SysUtils.h>
#include <utils/common/MsgHandler.h>
#include <utils/common/SUMOTime.h>
#include <utils/common/ToString.h>
template<class E, class V>
class SUMOAbstractRouter {
public:
class EdgeInfo {
public:
EdgeInfo(const E* const e)
: edge(e), effort(std::numeric_limits<double>::max()),
heuristicEffort(std::numeric_limits<double>::max()),
leaveTime(0.), prev(nullptr), visited(false), prohibited(false), prohibitionEnd(0) {}
const E* const edge;
double effort;
double heuristicEffort;
double leaveTime;
const EdgeInfo* prev;
bool visited;
bool prohibited;
double prohibitionEnd;
inline void reset() {
effort = std::numeric_limits<double>::max();
heuristicEffort = std::numeric_limits<double>::max();
visited = false;
}
};
typedef double(* Operation)(const E* const, const V* const, double);
typedef std::map<const E*, double> Prohibitions;
SUMOAbstractRouter(const std::string& type, bool unbuildIsWarning, Operation operation, Operation ttOperation,
const bool havePermissions, const bool haveRestrictions) :
myErrorMsgHandler(unbuildIsWarning ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
myOperation(operation), myTTOperation(ttOperation),
myBulkMode(false),
myAutoBulkMode(false),
myHavePermissions(havePermissions),
myHaveRestrictions(haveRestrictions),
myType(type),
myQueryVisits(0),
myNumQueries(0),
myQueryStartTime(0),
myQueryTimeSum(0) {
}
SUMOAbstractRouter(SUMOAbstractRouter* other) :
myErrorMsgHandler(other->myErrorMsgHandler),
myOperation(other->myOperation), myTTOperation(other->myTTOperation),
myBulkMode(false),
myAutoBulkMode(false),
myHavePermissions(other->myHavePermissions),
myHaveRestrictions(other->myHaveRestrictions),
myType(other->myType),
myQueryVisits(0),
myNumQueries(0),
myQueryStartTime(0),
myQueryTimeSum(0) { }
virtual ~SUMOAbstractRouter() {
if (myNumQueries > 0) {
WRITE_MESSAGE(myType + " answered " + toString(myNumQueries) + " queries and explored " + toString((double)myQueryVisits / (double)myNumQueries) + " edges on average.");
WRITE_MESSAGE(myType + " spent " + elapsedMs2string(myQueryTimeSum) + " answering queries (" + toString((double)myQueryTimeSum / (double)myNumQueries) + "ms on average).");
}
}
virtual SUMOAbstractRouter* clone() = 0;
inline void init(const int edgeID, const SUMOTime msTime) {
for (auto& edgeInfo : myFrontierList) {
edgeInfo->reset();
}
myFrontierList.clear();
for (auto& edgeInfo : myFound) {
edgeInfo->reset();
}
myFound.clear();
if (edgeID > -1) {
auto& fromInfo = myEdgeInfos[edgeID];
fromInfo.effort = 0.;
fromInfo.heuristicEffort = 0.;
fromInfo.prev = nullptr;
fromInfo.leaveTime = STEPS2TIME(msTime);
myFrontierList.push_back(&fromInfo);
}
myAmClean = true;
}
virtual void reset(const V* const vehicle) {
UNUSED_PARAMETER(vehicle);
}
const std::string& getType() const {
return myType;
}
inline const typename SUMOAbstractRouter<E, V>::EdgeInfo& getEdgeInfo(int index) const {
return myEdgeInfos[index];
}
virtual bool compute(const E* from, const E* to, const V* const vehicle,
SUMOTime msTime, std::vector<const E*>& into, bool silent = false) = 0;
inline bool compute(
const E* from, double fromPos,
const E* to, double toPos,
const V* const vehicle,
SUMOTime msTime, std::vector<const E*>& into, bool silent = false) {
if (from != to || fromPos <= toPos) {
return compute(from, to, vehicle, msTime, into, silent);
} else {
return computeLooped(from, to, vehicle, msTime, into, silent);
}
}
inline bool computeLooped(const E* from, const E* to, const V* const vehicle,
SUMOTime msTime, std::vector<const E*>& into, bool silent = false) {
if (from != to) {
return compute(from, to, vehicle, msTime, into, silent);
}
double minEffort = std::numeric_limits<double>::max();
std::vector<const E*> best;
const SUMOVehicleClass vClass = vehicle == 0 ? SVC_IGNORING : vehicle->getVClass();
for (const std::pair<const E*, const E*>& follower : from->getViaSuccessors(vClass)) {
std::vector<const E*> tmp;
compute(follower.first, to, vehicle, msTime, tmp, true);
if (tmp.size() > 0) {
double effort = recomputeCosts(tmp, vehicle, msTime);
if (effort < minEffort) {
minEffort = effort;
best = tmp;
}
}
}
if (minEffort != std::numeric_limits<double>::max()) {
into.push_back(from);
std::copy(best.begin(), best.end(), std::back_inserter(into));
return true;
} else if (!silent && myErrorMsgHandler != nullptr) {
myErrorMsgHandler->informf(TL("No connection between edge '%' and edge '%' found."), from->getID(), to->getID());
}
return false;
}
inline bool isProhibited(const E* const edge, const V* const vehicle) const {
return (myHavePermissions && edge->prohibits(vehicle)) || (myHaveRestrictions && edge->restricts(vehicle));
}
inline double getTravelTime(const E* const e, const V* const v, const double t, const double effort) const {
return myTTOperation == nullptr ? effort : (*myTTOperation)(e, v, t);
}
inline void updateViaEdgeCost(const E* viaEdge, const V* const v, double& time, double& effort, double& length) const {
while (viaEdge != nullptr && viaEdge->isInternal()) {
const double viaEffortDelta = this->getEffort(viaEdge, v, time);
time += getTravelTime(viaEdge, v, time, viaEffortDelta);
effort += viaEffortDelta;
length += viaEdge->getLength();
viaEdge = viaEdge->getViaSuccessors().front().second;
}
}
inline void updateViaCost(const E* const prev, const E* const e, const V* const v, double& time, double& effort, double& length) const {
if (prev != nullptr) {
for (const std::pair<const E*, const E*>& follower : prev->getViaSuccessors()) {
if (follower.first == e) {
updateViaEdgeCost(follower.second, v, time, effort, length);
break;
}
}
}
const double effortDelta = this->getEffort(e, v, time);
effort += effortDelta;
time += getTravelTime(e, v, time, effortDelta);
length += e->getLength();
}
bool isValid(const std::vector<const E*>& edges, const V* const v) const {
for (const E* const e : edges) {
if (isProhibited(e, v)) {
return false;
}
}
return true;
}
virtual double recomputeCosts(const std::vector<const E*>& edges, const V* const v, SUMOTime msTime, double* lengthp = nullptr) const {
double time = STEPS2TIME(msTime);
double effort = 0.;
double length = 0.;
if (lengthp == nullptr) {
lengthp = &length;
} else {
*lengthp = 0.;
}
const E* prev = nullptr;
for (const E* const e : edges) {
updateViaCost(prev, e, v, time, effort, *lengthp);
prev = e;
}
return effort;
}
inline double recomputeCostsPos(const std::vector<const E*>& edges, const V* const v, double fromPos, double toPos, SUMOTime msTime, double* lengthp = nullptr) const {
double effort = recomputeCosts(edges, v, msTime, lengthp);
if (!edges.empty()) {
double firstEffort = this->getEffort(edges.front(), v, STEPS2TIME(msTime));
double lastEffort = this->getEffort(edges.back(), v, STEPS2TIME(msTime));
effort -= firstEffort * fromPos / edges.front()->getLength();
effort -= lastEffort * (edges.back()->getLength() - toPos) / edges.back()->getLength();
}
return effort;
}
inline double setHint(const typename std::vector<const E*>::const_iterator routeBegin, const typename std::vector<const E*>::const_iterator routeEnd, const V* const v, SUMOTime msTime) {
double time = STEPS2TIME(msTime);
double effort = 0.;
double length = 0.;
const EdgeInfo* prev = &myEdgeInfos[(*routeBegin)->getNumericalID()];
init((*routeBegin)->getNumericalID(), msTime);
for (auto e = routeBegin + 1; e != routeEnd; ++e) {
if (isProhibited(*e, v)) {
return -1;
}
auto& edgeInfo = myEdgeInfos[(*e)->getNumericalID()];
edgeInfo.heuristicEffort = effort;
edgeInfo.prev = prev;
updateViaCost(prev->edge, *e, v, time, effort, length);
edgeInfo.effort = effort;
edgeInfo.leaveTime = time;
myFound.push_back(&edgeInfo);
prev = &edgeInfo;
#ifdef ROUTER_DEBUG_HINT
if (ROUTER_DEBUG_COND) {
std::cout << "DEBUG: hit=" << (*e)->getID()
<< " TT=" << edgeInfo.effort
<< " EF=" << this->getEffort(*e, v, edgeInfo.leaveTime)
<< " HT=" << edgeInfo.heuristicEffort << "\n";
}
#endif
}
return effort;
}
inline double getEffort(const E* const e, const V* const v, double t) const {
if (this->myProhibited.size() > 0 && myEdgeInfos.size() > 0 && myEdgeInfos[e->getNumericalID()].prohibitionEnd > 0) {
return (myEdgeInfos[e->getNumericalID()].prohibitionEnd - t) + (*myOperation)(e, v, myEdgeInfos[e->getNumericalID()].prohibitionEnd);
} else {
return (*myOperation)(e, v, t);
}
}
inline void startQuery() {
myNumQueries++;
myQueryStartTime = SysUtils::getCurrentMillis();
}
inline void endQuery(int visits) {
myQueryVisits += visits;
myQueryTimeSum += (SysUtils::getCurrentMillis() - myQueryStartTime);
}
virtual void setBulkMode(const bool mode) {
myBulkMode = mode;
}
inline void setAutoBulkMode(const bool mode) {
myAutoBulkMode = mode;
}
virtual void prohibit(const Prohibitions& toProhibit) {
for (auto item : this->myProhibited) {
myEdgeInfos[item.first->getNumericalID()].prohibited = false;
myEdgeInfos[item.first->getNumericalID()].prohibitionEnd = 0;
}
for (auto item : toProhibit) {
if (item.second >= 0 && item.second != std::numeric_limits<double>::max()) {
myEdgeInfos[item.first->getNumericalID()].prohibitionEnd = item.second;
} else {
myEdgeInfos[item.first->getNumericalID()].prohibited = true;
}
}
this->myProhibited = toProhibit;
}
void buildPathFrom(const typename SUMOAbstractRouter<E, V>::EdgeInfo* rbegin, std::vector<const E*>& edges) {
std::vector<const E*> tmp;
while (rbegin != nullptr) {
tmp.push_back(rbegin->edge);
rbegin = rbegin->prev;
}
std::copy(tmp.rbegin(), tmp.rend(), std::back_inserter(edges));
}
protected:
MsgHandler* const myErrorMsgHandler;
Operation myOperation;
Operation myTTOperation;
bool myBulkMode;
bool myAutoBulkMode;
bool myAmClean;
const bool myHavePermissions;
const bool myHaveRestrictions;
Prohibitions myProhibited;
std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo> myEdgeInfos;
std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo*> myFrontierList;
std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo*> myFound;
private:
const std::string myType;
long long int myQueryVisits;
long long int myNumQueries;
long long int myQueryStartTime;
long long int myQueryTimeSum;
private:
SUMOAbstractRouter& operator=(const SUMOAbstractRouter& s) = delete;
};