#pragma once
#include <config.h>
#include <cassert>
#define RailEdge_DEBUGID ""
#define RailEdge_DEBUG_COND(obj) (true)
#define REVERSAL_SLACK (POSITION_EPS + NUMERICAL_EPS)
template<class E, class V>
class RailEdge {
public:
typedef RailEdge<E, V> _RailEdge;
typedef std::vector<std::pair<const _RailEdge*, const _RailEdge*> > ConstEdgePairVector;
RailEdge(const E* orig) :
myNumericalID(orig->getNumericalID()),
myOriginal(orig),
myTurnaround(nullptr),
myIsVirtual(true)
{ }
RailEdge(const E* turnStart, const E* turnEnd, int numericalID) :
myNumericalID(numericalID),
myID("TrainReversal!" + turnStart->getID() + "->" + turnEnd->getID()),
myOriginal(nullptr),
myTurnaround(nullptr),
myIsVirtual(true),
myMaxLength(turnStart->getLength() - REVERSAL_SLACK),
myStartLength(turnStart->getLength() - REVERSAL_SLACK) {
myViaSuccessors.push_back(std::make_pair(turnEnd->getRailwayRoutingEdge(), nullptr));
}
virtual ~RailEdge() {
delete myTurnaround;
}
void update(double maxTrainLength, const std::vector<const E*>& replacementEdges) {
if (maxTrainLength > myMaxLength) {
myMaxLength = maxTrainLength;
myReplacementEdges = replacementEdges;
#ifdef RailEdge_DEBUG_INIT
std::cout << " update RailEdge " << getID() << " myMaxLength=" << myMaxLength << " repl=" << toString(myReplacementEdges) << "\n";
#endif
}
}
void addVirtualTurns(const E* forward, const E* backward,
std::vector<_RailEdge*>& railEdges, int& numericalID, double dist,
double maxTrainLength, const std::vector<const E*>& replacementEdges) {
#ifdef RailEdge_DEBUG_INIT
std::cout << "addVirtualTurns forward=" << forward->getID() << " backward=" << backward->getID() << " dist=" << dist
<< " maxLength=" << maxTrainLength << " repl=" << toString(replacementEdges) << "\n";
#endif
if (dist <= 0) {
return;
}
for (const E* prev : forward->getPredecessors()) {
if (prev == backward) {
continue;
}
const E* bidi = prev->getBidiEdge();
if (bidi != nullptr && backward->isConnectedTo(*bidi, SVC_IGNORING)) {
_RailEdge* prevRailEdge = prev->getRailwayRoutingEdge();
if (prevRailEdge->myTurnaround == nullptr) {
prevRailEdge->myTurnaround = new _RailEdge(prev, bidi, numericalID++);
prevRailEdge->myViaSuccessors.push_back(std::make_pair(prevRailEdge->myTurnaround, nullptr));
railEdges.push_back(prevRailEdge->myTurnaround);
#ifdef RailEdge_DEBUG_INIT
std::cout << " RailEdge " << prevRailEdge->getID() << " virtual turnaround " << prevRailEdge->myTurnaround->getID() << "\n";
#endif
}
bool notFound = true;
for (const E* r : replacementEdges) {
if (r == prev) {
notFound = false;
break;
}
}
if (notFound) {
prevRailEdge->myTurnaround->update(prev->getLength() + maxTrainLength - REVERSAL_SLACK, replacementEdges);
std::vector<const E*> replacementEdges2;
replacementEdges2.push_back(prev);
replacementEdges2.insert(replacementEdges2.end(), replacementEdges.begin(), replacementEdges.end());
addVirtualTurns(prev, bidi, railEdges, numericalID, dist - prev->getLength(),
maxTrainLength + prev->getLength(), replacementEdges2);
}
}
}
}
void init(std::vector<_RailEdge*>& railEdges, int& numericalID, double maxTrainLength) {
for (const auto& viaPair : myOriginal->getViaSuccessors()) {
if (viaPair.first == myOriginal->getBidiEdge()) {
if (myTurnaround == nullptr) {
myTurnaround = new _RailEdge(myOriginal, viaPair.first, numericalID++);
myViaSuccessors.push_back(std::make_pair(myTurnaround, nullptr));
railEdges.push_back(myTurnaround);
#ifdef RailEdge_DEBUG_INIT
std::cout << " added new turnaround " << myTurnaround->getID() << "\n";
#endif
}
#ifdef RailEdge_DEBUG_INIT
std::cout << "RailEdge " << getID() << " actual turnaround " << myTurnaround->getID() << "\n";
#endif
myTurnaround->myIsVirtual = false;
const double initialDist = MAX2(maxTrainLength - getLength(), POSITION_EPS);
addVirtualTurns(myOriginal, viaPair.first, railEdges, numericalID,
initialDist, getLength(), std::vector<const E*> {myOriginal});
} else {
myViaSuccessors.push_back(std::make_pair(viaPair.first->getRailwayRoutingEdge(),
viaPair.second == nullptr ? nullptr : viaPair.second->getRailwayRoutingEdge()));
}
}
#ifdef RailEdge_DEBUG_SUCCESSORS
std::cout << "RailEdge " << getID() << " successors=" << myViaSuccessors.size() << " orig=" << myOriginal->getViaSuccessors().size() << "\n";
for (const auto& viaPair : myViaSuccessors) {
std::cout << " " << viaPair.first->getID() << "\n";
}
#endif
}
inline int getNumericalID() const {
return myNumericalID;
}
const E* getOriginal() const {
return myOriginal;
}
const std::string& getID() const {
return myOriginal != nullptr ? myOriginal->getID() : myID;
}
void insertOriginalEdges(double length, std::vector<const E*>& into) const {
if (myOriginal != nullptr) {
into.push_back(myOriginal);
} else {
double seen = myStartLength;
int nPushed = 0;
if (seen >= length && !myIsVirtual) {
return;
}
for (const E* edge : myReplacementEdges) {
into.push_back(edge);
nPushed++;
seen += edge->getLength() - REVERSAL_SLACK;
if (seen >= length && edge->isConnectedTo(*edge->getBidiEdge(), SVC_IGNORING)) {
break;
}
}
const int last = (int)into.size() - 1;
for (int i = 0; i < nPushed; i++) {
into.push_back(into[last - i]->getBidiEdge());
}
}
}
double getLength() const {
return myOriginal == nullptr ? 0 : myOriginal->getLength();
}
bool isInternal() const {
return myOriginal->isInternal();
}
inline bool prohibits(const V* const vehicle) const {
#ifdef RailEdge_DEBUG_TURNS
if (myOriginal == nullptr && RailEdge_DEBUG_COND(vehicle)) {
std::cout << getID() << " maxLength=" << myMaxLength << " veh=" << vehicle->getID() << " length=" << vehicle->getLength() << "\n";
}
#endif
return vehicle->getLength() > myMaxLength || (myOriginal != nullptr && myOriginal->prohibits(vehicle));
}
inline bool restricts(const V* const vehicle) const {
return myOriginal != nullptr && myOriginal->restricts(vehicle);
}
const ConstEdgePairVector& getViaSuccessors(SUMOVehicleClass vClass = SVC_IGNORING, bool ignoreTransientPermissions = false) const {
if (vClass == SVC_IGNORING || myOriginal == nullptr || myOriginal->isTazConnector()) {
return myViaSuccessors;
}
#ifdef HAVE_FOX
FXMutexLock lock(mySuccessorMutex);
#endif
auto i = myClassesViaSuccessorMap.find(vClass);
if (i != myClassesViaSuccessorMap.end()) {
return i->second;
}
ConstEdgePairVector& result = myClassesViaSuccessorMap[vClass];
for (const auto& viaPair : myViaSuccessors) {
if (viaPair.first->myOriginal == nullptr
|| viaPair.first->myOriginal->isTazConnector()
|| myOriginal->isConnectedTo(*viaPair.first->myOriginal, vClass, ignoreTransientPermissions)) {
result.push_back(viaPair);
}
}
return result;
}
bool isVirtual() const {
return myIsVirtual;
}
private:
const int myNumericalID;
const std::string myID;
const E* myOriginal;
_RailEdge* myTurnaround;
bool myIsVirtual;
std::vector<const E*> myReplacementEdges;
double myMaxLength = std::numeric_limits<double>::max();
double myStartLength = 0;
mutable std::map<SUMOVehicleClass, ConstEdgePairVector> myClassesViaSuccessorMap;
mutable ConstEdgePairVector myViaSuccessors;
#ifdef HAVE_FOX
mutable FXMutex mySuccessorMutex;
#endif
};