1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    MSTrafficLightLogic.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Michael Behrisch
14 /// @date    Sept 2002
15 /// @version $Id$
16 ///
17 // The parent class for traffic light logics
18 /****************************************************************************/
19 
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <cassert>
27 #include <string>
28 #include <iostream>
29 #include <map>
30 #include <microsim/MSLink.h>
31 #include <microsim/MSLane.h>
32 #include <microsim/MSEventControl.h>
33 #include <microsim/MSJunctionLogic.h>
34 #include <microsim/MSNet.h>
35 #include <microsim/MSEdge.h>
36 #include <microsim/MSGlobals.h>
37 #include "MSTLLogicControl.h"
38 #include "MSTrafficLightLogic.h"
39 
40 
41 // ===========================================================================
42 // static value definitions
43 // ===========================================================================
44 const MSTrafficLightLogic::LaneVector MSTrafficLightLogic::myEmptyLaneVector;
45 
46 
47 // ===========================================================================
48 // member method definitions
49 // ===========================================================================
50 /* -------------------------------------------------------------------------
51  * member method definitions
52  * ----------------------------------------------------------------------- */
SwitchCommand(MSTLLogicControl & tlcontrol,MSTrafficLightLogic * tlLogic,SUMOTime nextSwitch)53 MSTrafficLightLogic::SwitchCommand::SwitchCommand(MSTLLogicControl& tlcontrol,
54         MSTrafficLightLogic* tlLogic, SUMOTime nextSwitch)
55     : myTLControl(tlcontrol), myTLLogic(tlLogic),
56       myAssumedNextSwitch(nextSwitch), myAmValid(true) {}
57 
58 
~SwitchCommand()59 MSTrafficLightLogic::SwitchCommand::~SwitchCommand() {}
60 
61 
62 
63 SUMOTime
execute(SUMOTime t)64 MSTrafficLightLogic::SwitchCommand::execute(SUMOTime t) {
65     // check whether this command has been descheduled
66     if (!myAmValid) {
67         return 0;
68     }
69     //
70     const bool isActive = myTLControl.isActive(myTLLogic);
71     int step1 = myTLLogic->getCurrentPhaseIndex();
72     SUMOTime next = myTLLogic->trySwitch();
73     int step2 = myTLLogic->getCurrentPhaseIndex();
74     if (step1 != step2) {
75         if (isActive) {
76             // execute any action connected to this tls
77             const MSTLLogicControl::TLSLogicVariants& vars = myTLControl.get(myTLLogic->getID());
78             // set link priorities
79             myTLLogic->setTrafficLightSignals(t);
80             // execute switch actions
81             vars.executeOnSwitchActions();
82         }
83     }
84     myAssumedNextSwitch += next;
85     return next;
86 }
87 
88 
89 void
deschedule(MSTrafficLightLogic * tlLogic)90 MSTrafficLightLogic::SwitchCommand::deschedule(MSTrafficLightLogic* tlLogic) {
91     if (tlLogic == myTLLogic) {
92         myAmValid = false;
93         myAssumedNextSwitch = -1;
94     }
95 }
96 
97 
98 /* -------------------------------------------------------------------------
99  * member method definitions
100  * ----------------------------------------------------------------------- */
MSTrafficLightLogic(MSTLLogicControl & tlcontrol,const std::string & id,const std::string & programID,const TrafficLightType logicType,const SUMOTime delay,const std::map<std::string,std::string> & parameters)101 MSTrafficLightLogic::MSTrafficLightLogic(MSTLLogicControl& tlcontrol, const std::string& id,
102         const std::string& programID, const TrafficLightType logicType, const SUMOTime delay,
103         const std::map<std::string, std::string>& parameters) :
104     Named(id), Parameterised(parameters),
105     myProgramID(programID),
106     myLogicType(logicType),
107     myCurrentDurationIncrement(-1),
108     myDefaultCycleTime(0) {
109     mySwitchCommand = new SwitchCommand(tlcontrol, this, delay);
110     MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(mySwitchCommand, delay);
111 }
112 
113 
114 void
init(NLDetectorBuilder &)115 MSTrafficLightLogic::init(NLDetectorBuilder&) {
116     const Phases& phases = getPhases();
117     if (phases.size() > 0 && MSGlobals::gMesoTLSPenalty > 0) {
118         initMesoTLSPenalties();
119     }
120     if (phases.size() > 1) {
121         bool haveWarnedAboutUnusedStates = false;
122         std::vector<bool> foundGreen(phases.front()->getState().size(), false);
123         for (int i = 0; i < (int)phases.size(); ++i) {
124             // warn about unused states
125             std::vector<int> nextPhases;
126             nextPhases.push_back((i + 1) % phases.size());
127             bool iNextDefault = true;
128             if (phases[i]->nextPhases.size() > 0) {
129                 nextPhases = phases[i]->nextPhases;
130                 iNextDefault = false;
131             }
132             for (int iNext : nextPhases) {
133                 if (iNext < 0 || iNext >= (int)phases.size()) {
134                     throw ProcessError("Invalid nextPhase " + toString(iNext) + " in tlLogic '" + getID()
135                                        + "', program '" + getProgramID() + "' with " + toString(phases.size()) + " phases");
136                 }
137                 const std::string optionalFrom = iNextDefault ? "" : " from phase " + toString(i);
138                 const std::string& state1 = phases[i]->getState();
139                 const std::string& state2 = phases[iNext]->getState();
140                 assert(state1.size() == state2.size());
141                 if (!haveWarnedAboutUnusedStates && state1.size() > myLanes.size() + myIgnoredIndices.size()) {
142                     WRITE_WARNING("Unused states in tlLogic '" + getID()
143                                   + "', program '" + getProgramID() + "' in phase " + toString(i)
144                                   + " after tl-index " + toString((int)myLanes.size() - 1));
145                     haveWarnedAboutUnusedStates = true;
146                 }
147                 // detect illegal states
148                 const std::string::size_type illegal = state1.find_first_not_of(SUMOXMLDefinitions::ALLOWED_TLS_LINKSTATES);
149                 if (std::string::npos != illegal) {
150                     throw ProcessError("Illegal character '" + toString(state1[illegal]) + "' in tlLogic '" + getID()
151                                        + "', program '" + getProgramID() + "' in phase " + toString(i));
152                 }
153                 // warn about transitions from green to red without intermediate yellow
154                 for (int j = 0; j < (int)MIN3(state1.size(), state2.size(), myLanes.size()); ++j) {
155                     if ((LinkState)state2[j] == LINKSTATE_TL_RED
156                             && ((LinkState)state1[j] == LINKSTATE_TL_GREEN_MAJOR
157                                 || (LinkState)state1[j] == LINKSTATE_TL_GREEN_MINOR)) {
158                         for (LaneVector::const_iterator it = myLanes[j].begin(); it != myLanes[j].end(); ++it) {
159                             if ((*it)->getPermissions() != SVC_PEDESTRIAN) {
160                                 WRITE_WARNING("Missing yellow phase in tlLogic '" + getID()
161                                               + "', program '" + getProgramID() + "' for tl-index " + toString(j)
162                                               + " when switching" + optionalFrom + " to phase " + toString(iNext));
163                                 return; // one warning per program is enough
164                             }
165                         }
166                     }
167                 }
168                 // warn about links that never get the green light
169                 for (int j = 0; j < (int)state1.size(); ++j) {
170                     LinkState ls = (LinkState)state1[j];
171                     if (ls == LINKSTATE_TL_GREEN_MAJOR || ls == LINKSTATE_TL_GREEN_MINOR) {
172                         foundGreen[j] = true;
173                     }
174                 }
175             }
176         }
177         for (int j = 0; j < (int)foundGreen.size(); ++j) {
178             if (!foundGreen[j]) {
179                 WRITE_WARNING("Missing green phase in tlLogic '" + getID()
180                               + "', program '" + getProgramID() + "' for tl-index " + toString(j));
181                 break;
182             }
183         }
184     }
185 }
186 
187 
~MSTrafficLightLogic()188 MSTrafficLightLogic::~MSTrafficLightLogic() {
189     // no need to do something about mySwitchCommand here,
190     // it is handled by the event control
191 }
192 
193 
194 // ----------- Handling of controlled links
195 void
addLink(MSLink * link,MSLane * lane,int pos)196 MSTrafficLightLogic::addLink(MSLink* link, MSLane* lane, int pos) {
197     // !!! should be done within the loader (checking necessary)
198     myLinks.reserve(pos + 1);
199     while ((int)myLinks.size() <= pos) {
200         myLinks.push_back(LinkVector());
201     }
202     myLinks[pos].push_back(link);
203     //
204     myLanes.reserve(pos + 1);
205     while ((int)myLanes.size() <= pos) {
206         myLanes.push_back(LaneVector());
207     }
208     myLanes[pos].push_back(lane);
209     link->setTLState((LinkState) getCurrentPhaseDef().getState()[pos], MSNet::getInstance()->getCurrentTimeStep());
210 }
211 
212 
213 void
adaptLinkInformationFrom(const MSTrafficLightLogic & logic)214 MSTrafficLightLogic::adaptLinkInformationFrom(const MSTrafficLightLogic& logic) {
215     myLinks = logic.myLinks;
216     myLanes = logic.myLanes;
217     myIgnoredIndices = logic.myIgnoredIndices;
218 }
219 
220 
221 std::map<MSLink*, LinkState>
collectLinkStates() const222 MSTrafficLightLogic::collectLinkStates() const {
223     std::map<MSLink*, LinkState> ret;
224     for (LinkVectorVector::const_iterator i1 = myLinks.begin(); i1 != myLinks.end(); ++i1) {
225         const LinkVector& l = (*i1);
226         for (LinkVector::const_iterator i2 = l.begin(); i2 != l.end(); ++i2) {
227             ret[*i2] = (*i2)->getState();
228         }
229     }
230     return ret;
231 }
232 
233 
234 bool
setTrafficLightSignals(SUMOTime t) const235 MSTrafficLightLogic::setTrafficLightSignals(SUMOTime t) const {
236     // get the current traffic light signal combination
237     const std::string& state = getCurrentPhaseDef().getState();
238     // go through the links
239     for (int i = 0; i < (int)myLinks.size(); i++) {
240         const LinkVector& currGroup = myLinks[i];
241         LinkState ls = (LinkState) state[i];
242         for (LinkVector::const_iterator j = currGroup.begin(); j != currGroup.end(); j++) {
243             (*j)->setTLState(ls, t);
244         }
245     }
246     return true;
247 }
248 
249 
250 void
resetLinkStates(const std::map<MSLink *,LinkState> & vals) const251 MSTrafficLightLogic::resetLinkStates(const std::map<MSLink*, LinkState>& vals) const {
252     for (LinkVectorVector::const_iterator i1 = myLinks.begin(); i1 != myLinks.end(); ++i1) {
253         const LinkVector& l = (*i1);
254         for (LinkVector::const_iterator i2 = l.begin(); i2 != l.end(); ++i2) {
255             assert(vals.find(*i2) != vals.end());
256             (*i2)->setTLState(vals.find(*i2)->second, MSNet::getInstance()->getCurrentTimeStep());
257         }
258     }
259 }
260 
261 
262 // ----------- Static Information Retrieval
263 int
getLinkIndex(const MSLink * const link) const264 MSTrafficLightLogic::getLinkIndex(const MSLink* const link) const {
265     int index = 0;
266     for (LinkVectorVector::const_iterator i1 = myLinks.begin(); i1 != myLinks.end(); ++i1, ++index) {
267         const LinkVector& l = (*i1);
268         for (LinkVector::const_iterator i2 = l.begin(); i2 != l.end(); ++i2) {
269             if ((*i2) == link) {
270                 return index;
271             }
272         }
273     }
274     return -1;
275 }
276 
277 
278 
279 // ----------- Dynamic Information Retrieval
280 SUMOTime
getNextSwitchTime() const281 MSTrafficLightLogic::getNextSwitchTime() const {
282     return mySwitchCommand != nullptr ? mySwitchCommand->getNextSwitchTime() : -1;
283 }
284 
285 
286 SUMOTime
getSpentDuration() const287 MSTrafficLightLogic::getSpentDuration() const {
288     const SUMOTime nextSwitch = getNextSwitchTime();
289     if (nextSwitch == -1) {
290         return -1;
291     } else {
292         const SUMOTime remaining = nextSwitch - MSNet::getInstance()->getCurrentTimeStep();
293         return getCurrentPhaseDef().duration - remaining;
294     }
295 }
296 
297 
298 // ----------- Changing phases and phase durations
299 void
addOverridingDuration(SUMOTime duration)300 MSTrafficLightLogic::addOverridingDuration(SUMOTime duration) {
301     myOverridingTimes.push_back(duration);
302 }
303 
304 
305 void
setCurrentDurationIncrement(SUMOTime delay)306 MSTrafficLightLogic::setCurrentDurationIncrement(SUMOTime delay) {
307     myCurrentDurationIncrement = delay;
308 }
309 
310 
initMesoTLSPenalties()311 void MSTrafficLightLogic::initMesoTLSPenalties() {
312     // set mesoscopic time penalties
313     const Phases& phases = getPhases();
314     const int numLinks = (int)myLinks.size();
315     // warning already given if not all states are used
316     assert(numLinks <= (int)phases.front()->getState().size());
317     SUMOTime duration = 0;
318     std::vector<double> redDuration(numLinks, 0);
319     std::vector<double> totalRedDuration(numLinks, 0);
320     std::vector<double> penalty(numLinks, 0);
321     for (int i = 0; i < (int)phases.size(); ++i) {
322         const std::string& state = phases[i]->getState();
323         duration += phases[i]->duration;
324         // warn about transitions from green to red without intermediate yellow
325         for (int j = 0; j < numLinks; ++j) {
326             if ((LinkState)state[j] == LINKSTATE_TL_RED
327                     || (LinkState)state[j] == LINKSTATE_TL_REDYELLOW) {
328                 redDuration[j] += STEPS2TIME(phases[i]->duration);
329                 totalRedDuration[j] += STEPS2TIME(phases[i]->duration);
330             } else if (redDuration[j] > 0) {
331                 penalty[j] += 0.5 * (redDuration[j] * redDuration[j] + redDuration[j]);
332                 redDuration[j] = 0;
333             }
334         }
335     }
336     /// XXX penalty for wrap-around red phases is underestimated
337     for (int j = 0; j < numLinks; ++j) {
338         if (redDuration[j] > 0) {
339             penalty[j] += 0.5 * (redDuration[j] * redDuration[j] + redDuration[j]);
340             redDuration[j] = 0;
341         }
342     }
343     const double durationSeconds = STEPS2TIME(duration);
344     std::set<const MSJunction*> controlledJunctions;
345     for (int j = 0; j < numLinks; ++j) {
346         for (int k = 0; k < (int)myLinks[j].size(); ++k) {
347             myLinks[j][k]->setMesoTLSPenalty(TIME2STEPS(MSGlobals::gMesoTLSPenalty * penalty[j] / durationSeconds));
348             myLinks[j][k]->setGreenFraction(MAX2((durationSeconds - MSGlobals::gMesoTLSPenalty * totalRedDuration[j]) / durationSeconds, NUMERICAL_EPS)); // avoid zero capacity (warning issued before)
349             controlledJunctions.insert(myLinks[j][k]->getLane()->getEdge().getFromJunction()); // MSLink::myJunction is not yet initialized
350             //std::cout << " tls=" << getID() << " i=" << j << " link=" << myLinks[j][k]->getViaLaneOrLane()->getID() << " penalty=" << penalty[j] / durationSeconds << " durSecs=" << durationSeconds << " greenTime=" << " gF=" << myLinks[j][k]->getGreenFraction() << "\n";
351         }
352     }
353     // initialize empty-net travel times
354     // XXX refactor after merging sharps (links know their incoming edge)
355     for (std::set<const MSJunction*>::iterator it = controlledJunctions.begin(); it != controlledJunctions.end(); ++it) {
356         const ConstMSEdgeVector incoming = (*it)->getIncoming();
357         for (ConstMSEdgeVector::const_iterator it_e = incoming.begin(); it_e != incoming.end(); ++it_e) {
358             const_cast<MSEdge*>(*it_e)->recalcCache();
359         }
360     }
361 
362 }
363 
364 
365 void
ignoreLinkIndex(int pos)366 MSTrafficLightLogic::ignoreLinkIndex(int pos) {
367     myIgnoredIndices.insert(pos);
368 }
369 
370 
371 bool
isSelected() const372 MSTrafficLightLogic::isSelected() const {
373     return MSNet::getInstance()->isSelected(this);
374 }
375 
376 /****************************************************************************/
377 
378