#include <config.h>
#include <vector>
#include <set>
#include <cassert>
#include <iterator>
#include <utils/common/MsgHandler.h>
#include <utils/common/ToString.h>
#include <utils/options/OptionsCont.h>
#include "NBTrafficLightLogic.h"
#include "NBTrafficLightDefinition.h"
#include "NBLoadedTLDef.h"
#include "NBNode.h"
NBLoadedTLDef::SignalGroup::SignalGroup(const std::string& id)
: Named(id) {}
NBLoadedTLDef::SignalGroup::~SignalGroup() {}
void
NBLoadedTLDef::SignalGroup::addConnection(const NBConnection& c) {
assert(c.getFromLane() < 0 || c.getFrom()->getNumLanes() > c.getFromLane());
myConnections.push_back(c);
}
void
NBLoadedTLDef::SignalGroup::addPhaseBegin(SUMOTime time, TLColor color) {
myPhases.push_back(PhaseDef(time, color));
}
void
NBLoadedTLDef::SignalGroup::setYellowTimes(SUMOTime tRedYellow, SUMOTime tYellow) {
myTRedYellow = tRedYellow;
myTYellow = tYellow;
}
void
NBLoadedTLDef::SignalGroup::sortPhases() {
std::sort(myPhases.begin(), myPhases.end(), [](const PhaseDef & p1, const PhaseDef & p2) {
return p1.myTime < p2.myTime;
});
}
void
NBLoadedTLDef::SignalGroup::patchTYellow(SUMOTime tyellow, bool forced) {
if (myTYellow < 0) {
myTYellow = tyellow;
} else if (forced && myTYellow < tyellow) {
WRITE_WARNINGF(TL("TYellow of signal group '%' was less than the computed one; patched (was:%, is:%)"), getID(), toString(myTYellow), time2string(tyellow));
myTYellow = tyellow;
}
}
std::vector<SUMOTime>
NBLoadedTLDef::SignalGroup::getTimes(SUMOTime cycleDuration) const {
std::vector<SUMOTime> ret;
for (const PhaseDef& p : myPhases) {
ret.push_back(p.myTime);
}
if (myTYellow > 0) {
for (const PhaseDef& p : myPhases) {
if (p.myColor == TLCOLOR_RED) {
ret.push_back((p.myTime + myTYellow) % cycleDuration);
}
}
}
return ret;
}
int
NBLoadedTLDef::SignalGroup::getLinkNo() const {
return (int) myConnections.size();
}
bool
NBLoadedTLDef::SignalGroup::mayDrive(SUMOTime time) const {
assert(myPhases.size() != 0);
for (std::vector<PhaseDef>::const_reverse_iterator i = myPhases.rbegin(); i != myPhases.rend(); i++) {
SUMOTime nextTime = (*i).myTime;
if (time >= nextTime) {
return (*i).myColor == TLCOLOR_GREEN;
}
}
return (*(myPhases.end() - 1)).myColor == TLCOLOR_GREEN;
}
bool
NBLoadedTLDef::SignalGroup::hasYellow(SUMOTime time) const {
bool has_red_now = !mayDrive(time);
bool had_green = mayDrive(time - myTYellow);
return has_red_now && had_green;
}
const NBConnection&
NBLoadedTLDef::SignalGroup::getConnection(int pos) const {
assert(pos < (int)myConnections.size());
return myConnections[pos];
}
bool
NBLoadedTLDef::SignalGroup::containsIncoming(NBEdge* from) const {
for (NBConnectionVector::const_iterator i = myConnections.begin(); i != myConnections.end(); i++) {
if ((*i).getFrom() == from) {
return true;
}
}
return false;
}
void
NBLoadedTLDef::SignalGroup::remapIncoming(NBEdge* which, const EdgeVector& by) {
NBConnectionVector newConns;
for (NBConnectionVector::iterator i = myConnections.begin(); i != myConnections.end();) {
if ((*i).getFrom() == which) {
NBConnection conn((*i).getFrom(), (*i).getTo());
i = myConnections.erase(i);
for (EdgeVector::const_iterator j = by.begin(); j != by.end(); j++) {
NBConnection curr(conn);
if (!curr.replaceFrom(which, *j)) {
throw ProcessError("Could not replace edge '" + which->getID() + "' by '" + (*j)->getID() + "'.\nUndefined...");
}
newConns.push_back(curr);
}
} else {
i++;
}
}
copy(newConns.begin(), newConns.end(),
back_inserter(myConnections));
}
bool
NBLoadedTLDef::SignalGroup::containsOutgoing(NBEdge* to) const {
for (NBConnectionVector::const_iterator i = myConnections.begin(); i != myConnections.end(); i++) {
if ((*i).getTo() == to) {
return true;
}
}
return false;
}
void
NBLoadedTLDef::SignalGroup::remapOutgoing(NBEdge* which, const EdgeVector& by) {
NBConnectionVector newConns;
for (NBConnectionVector::iterator i = myConnections.begin(); i != myConnections.end();) {
if ((*i).getTo() == which) {
NBConnection conn((*i).getFrom(), (*i).getTo());
i = myConnections.erase(i);
for (EdgeVector::const_iterator j = by.begin(); j != by.end(); j++) {
NBConnection curr(conn);
if (!curr.replaceTo(which, *j)) {
throw ProcessError("Could not replace edge '" + which->getID() + "' by '" + (*j)->getID() + "'.\nUndefined...");
}
newConns.push_back(curr);
}
} else {
i++;
}
}
copy(newConns.begin(), newConns.end(),
back_inserter(myConnections));
}
void
NBLoadedTLDef::SignalGroup::remap(NBEdge* removed, int removedLane,
NBEdge* by, int byLane) {
for (NBConnectionVector::iterator i = myConnections.begin(); i != myConnections.end(); i++) {
if ((*i).getTo() == removed
&&
((*i).getToLane() == removedLane
||
(*i).getToLane() == -1)) {
(*i).replaceTo(removed, removedLane, by, byLane);
} else if ((*i).getTo() == removed && removedLane == -1) {
(*i).replaceTo(removed, by);
}
if ((*i).getFrom() == removed
&&
((*i).getFromLane() == removedLane
||
(*i).getFromLane() == -1)) {
(*i).replaceFrom(removed, removedLane, by, byLane);
} else if ((*i).getFrom() == removed && removedLane == -1) {
(*i).replaceFrom(removed, by);
}
}
}
NBLoadedTLDef::NBLoadedTLDef(const NBEdgeCont& ec, const std::string& id,
const std::vector<NBNode*>& junctions, SUMOTime offset, TrafficLightType type) :
NBTrafficLightDefinition(id, junctions, DefaultProgramID, offset, type),
myEdgeCont(&ec) {
}
NBLoadedTLDef::NBLoadedTLDef(const NBEdgeCont& ec, const std::string& id, NBNode* junction, SUMOTime offset, TrafficLightType type) :
NBTrafficLightDefinition(id, junction, DefaultProgramID, offset, type),
myEdgeCont(&ec) {
}
NBLoadedTLDef::NBLoadedTLDef(const NBEdgeCont& ec, const std::string& id, SUMOTime offset, TrafficLightType type) :
NBTrafficLightDefinition(id, DefaultProgramID, offset, type),
myEdgeCont(&ec) {
}
NBLoadedTLDef::~NBLoadedTLDef() {
for (SignalGroupCont::iterator i = mySignalGroups.begin(); i != mySignalGroups.end(); ++i) {
delete (*i).second;
}
}
NBTrafficLightLogic*
NBLoadedTLDef::myCompute(int brakingTimeSeconds) {
MsgHandler::getWarningInstance()->clear();
std::set<SUMOTime> switchTimes;
int numSignals = 0;
for (const auto& i : mySignalGroups) {
NBLoadedTLDef::SignalGroup* const group = i.second;
group->sortPhases();
group->patchTYellow(TIME2STEPS(brakingTimeSeconds), OptionsCont::getOptions().getBool("tls.yellow.patch-small"));
const std::vector<SUMOTime> gtimes = group->getTimes(myCycleDuration);
switchTimes.insert(gtimes.begin(), gtimes.end());
numSignals += group->getLinkNo();
}
NBTrafficLightLogic* logic = new NBTrafficLightLogic(getID(), getProgramID(), numSignals, myOffset, myType);
SUMOTime prev = -1;
for (const SUMOTime l : switchTimes) {
if (prev != -1) {
logic->addStep(l - prev, buildPhaseState(prev));
}
prev = l;
}
logic->addStep(myCycleDuration + (*switchTimes.begin()) - prev, buildPhaseState(prev));
if (MsgHandler::getWarningInstance()->wasInformed()) {
WRITE_WARNINGF(TL("During computation of traffic light '%'."), getID());
}
logic->closeBuilding();
myNeedsContRelation.clear();
const bool controlledWithin = !OptionsCont::getOptions().getBool("tls.uncontrolled-within");
const std::vector<NBTrafficLightLogic::PhaseDefinition> phases = logic->getPhases();
for (std::vector<NBTrafficLightLogic::PhaseDefinition>::const_iterator it = phases.begin(); it != phases.end(); it++) {
const std::string state = (*it).state;
for (NBConnectionVector::const_iterator it1 = myControlledLinks.begin(); it1 != myControlledLinks.end(); it1++) {
const NBConnection& c1 = *it1;
const int i1 = c1.getTLIndex();
if (i1 == NBConnection::InvalidTlIndex || state[i1] != 'g' || c1.getFrom() == nullptr || c1.getTo() == nullptr) {
continue;
}
for (NBConnectionVector::const_iterator it2 = myControlledLinks.begin(); it2 != myControlledLinks.end(); it2++) {
const NBConnection& c2 = *it2;
const int i2 = c2.getTLIndex();
if (i2 != NBConnection::InvalidTlIndex
&& i2 != i1
&& (state[i2] == 'G' || state[i2] == 'g')
&& c2.getFrom() != nullptr && c2.getTo() != nullptr) {
const bool rightTurnConflict = NBNode::rightTurnConflict(
c1.getFrom(), c1.getTo(), c1.getFromLane(), c2.getFrom(), c2.getTo(), c2.getFromLane());
if (forbids(c2.getFrom(), c2.getTo(), c1.getFrom(), c1.getTo(), true, controlledWithin) || rightTurnConflict) {
myNeedsContRelation.insert(StreamPair(c1.getFrom(), c1.getTo(), c2.getFrom(), c2.getTo()));
}
}
}
}
}
myNeedsContRelationReady = true;
return logic;
}
void
NBLoadedTLDef::setTLControllingInformation() const {
for (NBConnectionVector::const_iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
const NBConnection& c = *it;
if (c.getTLIndex() != NBConnection::InvalidTlIndex) {
c.getFrom()->setControllingTLInformation(c, getID());
}
}
}
std::string
NBLoadedTLDef::buildPhaseState(const SUMOTime time) const {
int pos = 0;
std::string state;
for (SignalGroupCont::const_iterator i = mySignalGroups.begin(); i != mySignalGroups.end(); i++) {
SignalGroup* group = (*i).second;
int linkNo = group->getLinkNo();
bool mayDrive = group->mayDrive(time);
bool hasYellow = group->hasYellow(time);
char c = 'r';
if (mayDrive) {
c = 'g';
}
if (hasYellow) {
c = 'y';
}
for (int j = 0; j < linkNo; j++) {
const NBConnection& conn = group->getConnection(j);
NBConnection assConn(conn);
if (assConn.check(*myEdgeCont)) {
state = state + c;
++pos;
}
}
}
pos = 0;
for (SignalGroupCont::const_iterator i = mySignalGroups.begin(); i != mySignalGroups.end(); i++) {
SignalGroup* group = (*i).second;
int linkNo = group->getLinkNo();
for (int j = 0; j < linkNo; j++) {
const NBConnection& conn = group->getConnection(j);
NBConnection assConn(conn);
if (assConn.check(*myEdgeCont)) {
if (!mustBrake(assConn, state, pos)) {
if (state[pos] == 'g') {
state[pos] = 'G';
}
if (state[pos] == 'y') {
state[pos] = 'Y';
}
}
pos++;
}
}
}
return state;
}
bool
NBLoadedTLDef::mustBrake(const NBConnection& possProhibited,
const std::string& state,
int strmpos) const {
if (state[strmpos] != 'g' && state[strmpos] != 'G') {
return true;
}
int pos = 0;
for (SignalGroupCont::const_iterator i = mySignalGroups.begin(); i != mySignalGroups.end(); i++) {
SignalGroup* group = (*i).second;
int linkNo = group->getLinkNo();
for (int j = 0; j < linkNo; j++) {
const NBConnection& other = group->getConnection(j);
NBConnection possProhibitor(other);
if (possProhibitor.check(*myEdgeCont)) {
if (possProhibited.getFrom() == possProhibitor.getFrom()) {
pos++;
continue;
}
if (state[pos] == 'g' || state[pos] == 'G') {
if (NBTrafficLightDefinition::mustBrake(possProhibited, possProhibitor, true)) {
return true;
}
}
pos++;
}
}
}
return false;
}
void
NBLoadedTLDef::setParticipantsInformation() {
collectNodes();
collectEdges();
collectLinks();
}
void
NBLoadedTLDef::collectNodes() {
myControlledNodes.clear();
SignalGroupCont::const_iterator m;
for (m = mySignalGroups.begin(); m != mySignalGroups.end(); m++) {
SignalGroup* group = (*m).second;
int linkNo = group->getLinkNo();
for (int j = 0; j < linkNo; j++) {
const NBConnection& conn = group->getConnection(j);
NBEdge* edge = conn.getFrom();
NBNode* node = edge->getToNode();
myControlledNodes.push_back(node);
}
}
std::sort(myControlledNodes.begin(), myControlledNodes.end(), NBNode::nodes_by_id_sorter());
}
void
NBLoadedTLDef::collectLinks() {
myControlledLinks.clear();
for (EdgeVector::iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
NBEdge* incoming = *i;
int noLanes = incoming->getNumLanes();
for (int j = 0; j < noLanes; j++) {
std::vector<NBEdge::Connection> elv = incoming->getConnectionsFromLane(j);
for (std::vector<NBEdge::Connection>::iterator k = elv.begin(); k != elv.end(); k++) {
NBEdge::Connection el = *k;
if (el.toEdge != nullptr) {
myControlledLinks.push_back(NBConnection(incoming, j, el.toEdge, el.toLane));
}
}
}
}
int pos = 0;
for (SignalGroupCont::const_iterator m = mySignalGroups.begin(); m != mySignalGroups.end(); m++) {
SignalGroup* group = (*m).second;
int linkNo = group->getLinkNo();
for (int j = 0; j < linkNo; j++) {
const NBConnection& conn = group->getConnection(j);
assert(conn.getFromLane() < 0 || (int) conn.getFrom()->getNumLanes() > conn.getFromLane());
NBConnection tst(conn);
tst.setTLIndex(pos);
if (tst.check(*myEdgeCont)) {
if (tst.getFrom()->mayBeTLSControlled(tst.getFromLane(), tst.getTo(), tst.getToLane())) {
for (NBConnectionVector::iterator it = myControlledLinks.begin(); it != myControlledLinks.end(); it++) {
NBConnection& c = *it;
if (c.getTLIndex() == NBConnection::InvalidTlIndex
&& tst.getFrom() == c.getFrom() && tst.getTo() == c.getTo()
&& (tst.getFromLane() < 0 || tst.getFromLane() == c.getFromLane())
&& (tst.getToLane() < 0 || tst.getToLane() == c.getToLane())) {
c.setTLIndex(pos);
}
}
pos++;
}
} else {
WRITE_WARNINGF(TL("Could not set signal on connection (signal: %, group: %)"), getID(), group->getID());
}
}
}
}
bool
NBLoadedTLDef::addToSignalGroup(const std::string& groupid,
const NBConnection& connection) {
if (mySignalGroups.find(groupid) == mySignalGroups.end()) {
return false;
}
mySignalGroups[groupid]->addConnection(connection);
NBNode* n1 = connection.getFrom()->getToNode();
if (n1 != nullptr) {
addNode(n1);
n1->addTrafficLight(this);
}
NBNode* n2 = connection.getTo()->getFromNode();
if (n2 != nullptr) {
addNode(n2);
n2->addTrafficLight(this);
}
return true;
}
bool
NBLoadedTLDef::addToSignalGroup(const std::string& groupid,
const NBConnectionVector& connections) {
bool ok = true;
for (NBConnectionVector::const_iterator i = connections.begin(); i != connections.end(); i++) {
ok &= addToSignalGroup(groupid, *i);
}
return ok;
}
void
NBLoadedTLDef::addSignalGroup(const std::string& id) {
assert(mySignalGroups.find(id) == mySignalGroups.end());
mySignalGroups[id] = new SignalGroup(id);
}
void
NBLoadedTLDef::addSignalGroupPhaseBegin(const std::string& groupid, SUMOTime time,
TLColor color) {
assert(mySignalGroups.find(groupid) != mySignalGroups.end());
mySignalGroups[groupid]->addPhaseBegin(time, color);
}
void
NBLoadedTLDef::setSignalYellowTimes(const std::string& groupid,
SUMOTime myTRedYellow, SUMOTime myTYellow) {
assert(mySignalGroups.find(groupid) != mySignalGroups.end());
mySignalGroups[groupid]->setYellowTimes(myTRedYellow, myTYellow);
}
void
NBLoadedTLDef::setCycleDuration(SUMOTime cycleDur) {
myCycleDuration = cycleDur;
}
void
NBLoadedTLDef::remapRemoved(NBEdge* removed,
const EdgeVector& incoming,
const EdgeVector& outgoing) {
for (SignalGroupCont::const_iterator i = mySignalGroups.begin(); i != mySignalGroups.end(); i++) {
SignalGroup* group = (*i).second;
if (group->containsIncoming(removed)) {
group->remapIncoming(removed, incoming);
}
if (group->containsOutgoing(removed)) {
group->remapOutgoing(removed, outgoing);
}
}
}
void
NBLoadedTLDef::replaceRemoved(NBEdge* removed, int removedLane,
NBEdge* by, int byLane, bool incoming) {
for (SignalGroupCont::const_iterator i = mySignalGroups.begin(); i != mySignalGroups.end(); i++) {
SignalGroup* group = (*i).second;
if ((incoming && group->containsIncoming(removed)) || (!incoming && group->containsOutgoing(removed))) {
group->remap(removed, removedLane, by, byLane);
}
}
}
void
NBLoadedTLDef::initNeedsContRelation() const {
if (!myNeedsContRelationReady) {
throw ProcessError(TL("myNeedsContRelation was not properly initialized\n"));
}
}
int
NBLoadedTLDef::getMaxIndex() {
setParticipantsInformation();
NBTrafficLightLogic* logic = compute(OptionsCont::getOptions());
if (logic != nullptr) {
return logic->getNumLinks() - 1;
} else {
return -1;
}
}