Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file MSSimpleTrafficLightLogic.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Julia Ringel
17 : /// @author Jakob Erdmann
18 : /// @author Michael Behrisch
19 : /// @author Friedemann Wesner
20 : /// @date Sept 2002
21 : ///
22 : // A fixed traffic light logic
23 : /****************************************************************************/
24 : #include <config.h>
25 :
26 : #include <cassert>
27 : #include <utility>
28 : #include <vector>
29 : #include <bitset>
30 : #include <sstream>
31 : #include <utils/common/StringUtils.h>
32 : #include <microsim/MSEventControl.h>
33 : #include <microsim/MSNet.h>
34 : #include <microsim/MSEventControl.h>
35 : #include "MSTLLogicControl.h"
36 : #include "MSTrafficLightLogic.h"
37 : #include "MSSimpleTrafficLightLogic.h"
38 :
39 : //#define DEBUG_COORDINATION
40 : #define DEBUG_COND (getID()=="C")
41 :
42 :
43 : // ===========================================================================
44 : // member method definitions
45 : // ===========================================================================
46 120384 : MSSimpleTrafficLightLogic::MSSimpleTrafficLightLogic(MSTLLogicControl& tlcontrol,
47 : const std::string& id, const std::string& programID, const SUMOTime offset, const TrafficLightType logicType, const Phases& phases,
48 : int step, SUMOTime delay,
49 120384 : const Parameterised::Map& parameters) :
50 : MSTrafficLightLogic(tlcontrol, id, programID, offset, logicType, delay, parameters),
51 120384 : myPhases(phases),
52 120384 : myStep(step) {
53 120384 : myDefaultCycleTime = computeCycleTime(myPhases);
54 120384 : if (myStep < (int)myPhases.size()) {
55 120291 : myPhases[myStep]->myLastSwitch = SIMSTEP;
56 : }
57 : // the following initializations are only used by 'actuated' and 'delay_based' but do not affect 'static'
58 240768 : if (hasParameter(toString(SUMO_ATTR_CYCLETIME))) {
59 66 : myDefaultCycleTime = TIME2STEPS(StringUtils::toDouble(Parameterised::getParameter(toString(SUMO_ATTR_CYCLETIME), "")));
60 : }
61 240768 : myCoordinated = StringUtils::toBool(Parameterised::getParameter("coordinated", "false"));
62 120384 : if (myPhases.size() > 0) {
63 120291 : SUMOTime earliest = SIMSTEP + getEarliest(-1);
64 120291 : if (earliest > getNextSwitchTime()) {
65 68 : mySwitchCommand->deschedule(this);
66 68 : mySwitchCommand = new SwitchCommand(tlcontrol, this, earliest);
67 68 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(mySwitchCommand, earliest);
68 : }
69 : }
70 120384 : }
71 :
72 :
73 238981 : MSSimpleTrafficLightLogic::~MSSimpleTrafficLightLogic() {
74 120180 : deletePhases();
75 238981 : }
76 :
77 :
78 : // ------------ Switching and setting current rows
79 : SUMOTime
80 5897494 : MSSimpleTrafficLightLogic::trySwitch() {
81 : // check whether the current duration shall be increased
82 5897494 : if (myCurrentDurationIncrement > 0) {
83 : SUMOTime delay = myCurrentDurationIncrement;
84 0 : myCurrentDurationIncrement = 0;
85 0 : return delay;
86 : }
87 :
88 : // increment the index
89 5897494 : if (myPhases[myStep]->nextPhases.size() > 0 && myPhases[myStep]->nextPhases.front() >= 0) {
90 444 : myStep = myPhases[myStep]->nextPhases.front();
91 : } else {
92 5897050 : myStep++;
93 : }
94 : // if the last phase was reached ...
95 5897494 : if (myStep >= (int)myPhases.size()) {
96 : // ... set the index to the first phase
97 2497370 : myStep = 0;
98 : }
99 : assert((int)myPhases.size() > myStep);
100 : //stores the time the phase started
101 5897494 : myPhases[myStep]->myLastSwitch = MSNet::getInstance()->getCurrentTimeStep();
102 : // check whether the next duration was overridden
103 5897494 : if (myOverridingTimes.size() > 0) {
104 0 : SUMOTime nextDuration = myOverridingTimes[0];
105 0 : myOverridingTimes.erase(myOverridingTimes.begin());
106 0 : return nextDuration;
107 : }
108 : // return offset to the next switch
109 5897494 : return myPhases[myStep]->duration;
110 : }
111 :
112 :
113 : // ------------ Static Information Retrieval
114 : int
115 7811 : MSSimpleTrafficLightLogic::getPhaseNumber() const {
116 7811 : return (int) myPhases.size();
117 : }
118 :
119 :
120 : const MSSimpleTrafficLightLogic::Phases&
121 266177 : MSSimpleTrafficLightLogic::getPhases() const {
122 266177 : return myPhases;
123 : }
124 :
125 :
126 : MSSimpleTrafficLightLogic::Phases&
127 1643 : MSSimpleTrafficLightLogic::getPhases() {
128 1643 : return myPhases;
129 : }
130 :
131 :
132 : const MSPhaseDefinition&
133 114658 : MSSimpleTrafficLightLogic::getPhase(int givenStep) const {
134 : assert((int)myPhases.size() > givenStep);
135 114658 : return *myPhases[givenStep];
136 : }
137 :
138 :
139 : // ------------ Dynamic Information Retrieval
140 : int
141 15335087 : MSSimpleTrafficLightLogic::getCurrentPhaseIndex() const {
142 15335087 : return myStep;
143 : }
144 :
145 :
146 : const MSPhaseDefinition&
147 21696664 : MSSimpleTrafficLightLogic::getCurrentPhaseDef() const {
148 21696664 : return *myPhases[myStep];
149 : }
150 :
151 :
152 : void
153 515 : MSSimpleTrafficLightLogic::resetLastSwitch(SUMOTime t) {
154 515 : myPhases[myStep]->myLastSwitch = t;
155 515 : }
156 :
157 :
158 : // ------------ Conversion between time and phase
159 : SUMOTime
160 0 : MSSimpleTrafficLightLogic::getPhaseIndexAtTime(SUMOTime simStep) const {
161 : SUMOTime position = 0;
162 0 : if (myStep > 0) {
163 0 : for (int i = 0; i < myStep; i++) {
164 0 : position = position + getPhase(i).duration;
165 : }
166 : }
167 0 : position = position + simStep - getPhase(myStep).myLastSwitch;
168 0 : position = position % myDefaultCycleTime;
169 : assert(position <= myDefaultCycleTime);
170 0 : return position;
171 : }
172 :
173 :
174 : SUMOTime
175 1312 : MSSimpleTrafficLightLogic::getOffsetFromIndex(int index) const {
176 : assert(index < (int)myPhases.size());
177 1312 : if (index == 0) {
178 : return 0;
179 : }
180 : SUMOTime pos = 0;
181 3080 : for (int i = 0; i < index; i++) {
182 2224 : pos += getPhase(i).duration;
183 : }
184 : return pos;
185 : }
186 :
187 :
188 : int
189 32 : MSSimpleTrafficLightLogic::getIndexFromOffset(SUMOTime offset) const {
190 32 : offset = offset % myDefaultCycleTime;
191 32 : if (offset == myDefaultCycleTime) {
192 : return 0;
193 : }
194 : SUMOTime testPos = 0;
195 32 : for (int i = 0; i < (int)myPhases.size(); i++) {
196 32 : testPos = testPos + getPhase(i).duration;
197 32 : if (testPos > offset) {
198 32 : return i;
199 : }
200 0 : if (testPos == offset) {
201 : assert((int)myPhases.size() > (i + 1));
202 0 : return (i + 1);
203 : }
204 : }
205 : return 0;
206 : }
207 :
208 :
209 : SUMOTime
210 407732 : MSSimpleTrafficLightLogic::mapTimeInCycle(SUMOTime t) const {
211 407732 : return (myCoordinated
212 407732 : ? (t - myOffset) % myDefaultCycleTime
213 171227 : : (t - myPhases[0]->myLastSwitch) % myDefaultCycleTime);
214 : }
215 :
216 :
217 :
218 :
219 : SUMOTime
220 434926 : MSSimpleTrafficLightLogic::getEarliest(SUMOTime prevStart) const {
221 434926 : SUMOTime earliest = getEarliestEnd();
222 434926 : if (earliest == MSPhaseDefinition::UNSPECIFIED_DURATION) {
223 : return 0;
224 : } else {
225 10996 : if (prevStart >= SIMSTEP - getTimeInCycle() && prevStart < getCurrentPhaseDef().myLastEnd) {
226 : // phase was started and ended once already in the current cycle
227 : // it should not end a second time in the same cycle
228 612 : earliest += myDefaultCycleTime;
229 : #ifdef DEBUG_COORDINATION
230 : if (DEBUG_COND) {
231 : std::cout << SIMTIME << " tl=" << getID() << " getEarliest phase=" << myStep
232 : << " prevStart= " << STEPS2TIME(prevStart)
233 : << " prevEnd= " << STEPS2TIME(getCurrentPhaseDef().myLastEnd)
234 : << " cycleStart=" << STEPS2TIME(SIMSTEP - getTimeInCycle()) << " started Twice - move into next cycle\n";
235 : }
236 : #endif
237 : } else {
238 10384 : SUMOTime latest = getLatestEnd();
239 10384 : if (latest != MSPhaseDefinition::UNSPECIFIED_DURATION) {
240 7626 : const SUMOTime minRemaining = getMinDur() - (SIMSTEP - getCurrentPhaseDef().myLastSwitch);
241 7626 : const SUMOTime minEnd = getTimeInCycle() + minRemaining;
242 7626 : if (latest > earliest && latest < minEnd) {
243 : // cannot terminate phase between earliest and latest -> move end into next cycle
244 244 : earliest += myDefaultCycleTime;
245 7382 : } else if (latest < earliest && latest >= minEnd) {
246 : // can ignore earliest since it counts from the previous cycle
247 358 : earliest -= myDefaultCycleTime;
248 : }
249 : #ifdef DEBUG_COORDINATION
250 : if (DEBUG_COND) {
251 : std::cout << SIMTIME << " tl=" << getID() << " getEarliest phase=" << myStep << " latest=" << STEPS2TIME(latest) << " minEnd="
252 : << STEPS2TIME(minEnd) << " earliest=" << STEPS2TIME(earliest) << "\n";
253 : }
254 : #endif
255 : }
256 : }
257 10996 : const SUMOTime maxRemaining = getMaxDur() - (SIMSTEP - getCurrentPhaseDef().myLastSwitch);
258 10996 : return MIN2(earliest - getTimeInCycle(), maxRemaining);
259 : }
260 : }
261 :
262 :
263 : SUMOTime
264 1629353 : MSSimpleTrafficLightLogic::getLatest() const {
265 1629353 : const SUMOTime latest = getLatestEnd();
266 1629353 : if (latest == MSPhaseDefinition::UNSPECIFIED_DURATION) {
267 : return SUMOTime_MAX; // no restriction
268 : } else {
269 337914 : if (latest < getEarliestEnd()) {
270 38802 : const SUMOTime running = SIMSTEP - getCurrentPhaseDef().myLastSwitch;
271 38802 : if (running < getTimeInCycle()) {
272 : // phase was started in the current cycle so the restriction does not apply yet
273 : return SUMOTime_MAX;
274 : }
275 : }
276 : #ifdef DEBUG_COORDINATION
277 : if (DEBUG_COND) {
278 : std::cout << SIMTIME << " tl=" << getID() << " getLatest phase=" << myStep << " latest=" << STEPS2TIME(latest)
279 : << " cycTime=" << STEPS2TIME(getTimeInCycle()) << " res=" << STEPS2TIME(latest - getTimeInCycle()) << "\n";
280 : }
281 : #endif
282 310698 : if (latest == myDefaultCycleTime && getTimeInCycle() == 0) {
283 : // special case: end on cylce time wrap-around
284 : return 0;
285 : }
286 310626 : return MAX2(SUMOTime(0), latest - getTimeInCycle());
287 : }
288 : }
289 :
290 :
291 :
292 : // ------------ Changing phases and phase durations
293 : void
294 3748 : MSSimpleTrafficLightLogic::changeStepAndDuration(MSTLLogicControl& tlcontrol,
295 : SUMOTime simStep, int step, SUMOTime stepDuration) {
296 3748 : mySwitchCommand->deschedule(this);
297 3748 : mySwitchCommand = new SwitchCommand(tlcontrol, this, stepDuration + simStep);
298 3748 : if (step >= 0 && step != myStep) {
299 1489 : myStep = step;
300 1489 : myPhases[myStep]->myLastSwitch = MSNet::getInstance()->getCurrentTimeStep();
301 1489 : if (myAmActive) {
302 : // when loading from state, the last loaded program isn't always the active one
303 1467 : setTrafficLightSignals(simStep);
304 : }
305 1489 : tlcontrol.get(getID()).executeOnSwitchActions();
306 : }
307 3748 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(
308 3748 : mySwitchCommand, stepDuration + simStep);
309 3748 : }
310 :
311 :
312 : void
313 23 : MSSimpleTrafficLightLogic::setPhases(const Phases& phases, int step) {
314 : assert(step < (int)phases.size());
315 23 : SUMOTime lastSwitch = myPhases[myStep]->getState() == phases[step]->getState() ? myPhases[myStep]->myLastSwitch : SIMSTEP;
316 23 : deletePhases();
317 23 : myPhases = phases;
318 23 : myStep = step;
319 23 : myDefaultCycleTime = computeCycleTime(myPhases);
320 23 : myPhases[myStep]->myLastSwitch = lastSwitch;
321 23 : }
322 :
323 :
324 : void
325 120203 : MSSimpleTrafficLightLogic::deletePhases() {
326 729398 : for (int i = 0; i < (int)myPhases.size(); i++) {
327 609195 : delete myPhases[i];
328 : }
329 120203 : }
330 :
331 :
332 : void
333 1896 : MSSimpleTrafficLightLogic::saveStateAttrs(OutputDevice& out) const {
334 1896 : out.writeAttr(SUMO_ATTR_ID, getID());
335 1896 : out.writeAttr(SUMO_ATTR_PROGRAMID, getProgramID());
336 1896 : out.writeAttr(SUMO_ATTR_PHASE, getCurrentPhaseIndex());
337 1896 : out.writeAttr(SUMO_ATTR_DURATION, getSpentDuration());
338 1896 : out.writeAttr(SUMO_ATTR_UNTIL, getNextSwitchTime());
339 1896 : out.writeAttr(SUMO_ATTR_CYCLETIME, getTimeInCycle());
340 1896 : out.writeAttr(SUMO_ATTR_ACTIVE, myAmActive);
341 1896 : }
342 :
343 :
344 : void
345 1894 : MSSimpleTrafficLightLogic::saveState(OutputDevice& out) const {
346 1894 : out.openTag(SUMO_TAG_TLLOGIC);
347 1894 : saveStateAttrs(out);
348 1894 : out.closeTag();
349 1894 : }
350 :
351 :
352 : const std::string
353 1156872 : MSSimpleTrafficLightLogic::getParameter(const std::string& key, const std::string defaultValue) const {
354 1156872 : if (key == "cycleTime") {
355 10 : return toString(STEPS2TIME(myDefaultCycleTime));
356 1156862 : } else if (key == "offset") {
357 10 : return toString(STEPS2TIME(myOffset));
358 1156852 : } else if (key == "coordinated") {
359 10 : return toString(myCoordinated);
360 1156842 : } else if (key == "cycleSecond") {
361 10 : return toString(STEPS2TIME(getTimeInCycle()));
362 1156832 : } else if (key == "typeName") {
363 15 : return toString(this->getLogicType());
364 : }
365 2313634 : return Parameterised::getParameter(key, defaultValue);
366 : }
367 :
368 : void
369 126 : MSSimpleTrafficLightLogic::setParameter(const std::string& key, const std::string& value) {
370 126 : if (key == "cycleTime") {
371 5 : myDefaultCycleTime = string2time(value);
372 5 : Parameterised::setParameter(key, value);
373 121 : } else if (key == "cycleSecond" || key == "typeName") {
374 0 : throw InvalidArgument(key + " cannot be changed dynamically for traffic light '" + getID() + "'");
375 121 : } else if (key == "offset") {
376 5 : myOffset = string2time(value);
377 116 : } else if (key == "coordinated") {
378 5 : myCoordinated = StringUtils::toBool(value);
379 5 : Parameterised::setParameter(key, value);
380 : } else {
381 111 : Parameterised::setParameter(key, value);
382 : }
383 126 : }
384 :
385 : /****************************************************************************/
|