View Javadoc
1   package org.opentrafficsim.trafficcontrol;
2   
3   import java.rmi.RemoteException;
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.LinkedHashMap;
7   import java.util.LinkedHashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import org.djunits.value.vdouble.scalar.Duration;
13  import org.djunits.value.vdouble.scalar.Time;
14  import org.djutils.base.Identifiable;
15  import org.djutils.event.Event;
16  import org.djutils.exceptions.Throw;
17  import org.djutils.immutablecollections.Immutable;
18  import org.djutils.immutablecollections.ImmutableArrayList;
19  import org.djutils.immutablecollections.ImmutableHashSet;
20  import org.djutils.immutablecollections.ImmutableList;
21  import org.djutils.immutablecollections.ImmutableMap;
22  import org.djutils.immutablecollections.ImmutableSet;
23  import org.opentrafficsim.core.dsol.OtsSimulatorInterface;
24  import org.opentrafficsim.core.network.Network;
25  import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLight;
26  import org.opentrafficsim.road.network.lane.object.trafficlight.TrafficLightColor;
27  
28  import nl.tudelft.simulation.dsol.SimRuntimeException;
29  
30  /**
31   * Fixed time traffic light control.
32   * <p>
33   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
34   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
35   * </p>
36   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
37   * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
38   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
39   */
40  public class FixedTimeController extends AbstractTrafficController
41  {
42  
43      /** */
44      private static final long serialVersionUID = 20190221L;
45  
46      /** Cycle time. */
47      private final Duration cycleTime;
48  
49      /** Offset. */
50      private final Duration offset;
51  
52      /** Signal groups, for cloning. */
53      private final Set<SignalGroup> signalGroups;
54  
55      /**
56       * Constructor for fixed time traffic controller.
57       * @param id String; id
58       * @param simulator OtsSimulatorInterface; simulator
59       * @param network Network; network
60       * @param offset Duration; off set from simulation start time
61       * @param cycleTime Duration; cycle time
62       * @param signalGroups Set&lt;SignalGroup&gt;; signal groups
63       * @throws SimRuntimeException simulator is past zero time
64       */
65      public FixedTimeController(final String id, final OtsSimulatorInterface simulator, final Network network,
66              final Duration cycleTime, final Duration offset, final Set<SignalGroup> signalGroups) throws SimRuntimeException
67      {
68          super(id, simulator);
69          Throw.whenNull(simulator, "Simulator may not be null.");
70          Throw.whenNull(network, "Network may not be null.");
71          Throw.whenNull(cycleTime, "Cycle time may not be null.");
72          Throw.whenNull(offset, "Offset may not be null.");
73          Throw.whenNull(signalGroups, "Signal groups may not be null.");
74          Throw.when(cycleTime.le0(), IllegalArgumentException.class, "Cycle time must be positive.");
75          // Throw.when(signalGroups.isEmpty(), IllegalArgumentException.class, "Signal groups may not be empty.");
76          /*- This is no longer considered an error
77          for (SignalGroup signalGroup1 : signalGroups)
78          {
79              for (SignalGroup signalGroup2 : signalGroups)
80              {
81                  if (!signalGroup1.equals(signalGroup2))
82                  {
83                      Throw.when(!ImmutableCollections.disjoint(signalGroup1.trafficLightIds, signalGroup2.trafficLightIds),
84                              IllegalArgumentException.class, "A traffic light is in both signal group %s and signal group %s.",
85                              signalGroup1.getId(), signalGroup2.getId());
86                  }
87              }
88          }
89          */
90          this.cycleTime = cycleTime;
91          this.offset = offset;
92          this.signalGroups = new LinkedHashSet<>(signalGroups); // make a copy so we can modify it.
93          mergeGreenPhasesInNewSignalGroups();
94          // Schedule setup at time == 0 (when the network should be fully created and all traffic lights have been constructed)
95          simulator.scheduleEventAbsTime(Time.ZERO, this, "setup", new Object[] {simulator, network});
96      }
97  
98      /**
99       * This method finds traffic lights that are present in multiple signal groups, extracts them from their existing groups,
100      * and places them in a new signal group that allows the traffic light to be green whenever any of the original signal
101      * groups did.
102      */
103     private void mergeGreenPhasesInNewSignalGroups()
104     {
105         // Identify traffic lights that are present in more than one signal group
106         Map<String, List<SignalGroup>> signalGroupsOfTrafficLight = new LinkedHashMap<>();
107         for (SignalGroup sg : this.signalGroups)
108         {
109             for (String trafficLightId : sg.getTrafficLightIds())
110             {
111                 List<SignalGroup> sgList = signalGroupsOfTrafficLight.get(trafficLightId);
112                 if (null == sgList)
113                 {
114                     sgList = new ArrayList<>();
115                     signalGroupsOfTrafficLight.put(trafficLightId, sgList);
116                 }
117                 sgList.add(sg);
118             }
119         }
120         // Collect all flanks that persist for nonzero duration
121         int nextNumber = 0;
122         for (String trafficLightId : signalGroupsOfTrafficLight.keySet())
123         {
124             List<SignalGroup> sgList = signalGroupsOfTrafficLight.get(trafficLightId);
125             if (sgList.size() > 1)
126             {
127                 // Check for overlapping or adjacent green phases
128                 List<Flank> flanks = new ArrayList<>();
129                 for (SignalGroup sg : sgList)
130                 {
131                     double sgOffset = sg.getOffset().si;
132                     double preGreenDuration = sg.getPreGreen().si;
133                     if (preGreenDuration > 0)
134                     {
135                         flanks.add(new Flank(sgOffset % this.cycleTime.si, TrafficLightColor.PREGREEN));
136                         sgOffset += preGreenDuration;
137                     }
138                     flanks.add(new Flank(sgOffset % this.cycleTime.si, TrafficLightColor.GREEN));
139                     sgOffset += sg.getGreen().si;
140                     double yellowDuration = sg.getYellow().si;
141                     if (yellowDuration > 0)
142                     {
143                         flanks.add(new Flank(sgOffset % this.cycleTime.si, TrafficLightColor.YELLOW));
144                         sgOffset += yellowDuration;
145                     }
146                     flanks.add(new Flank(sgOffset % this.cycleTime.si, TrafficLightColor.RED));
147                 }
148                 Collections.sort(flanks);
149                 boolean combined = false;
150                 int greenCount = 0;
151                 for (int index = 0; index < flanks.size(); index++)
152                 {
153                     Flank flank = flanks.get(index);
154                     TrafficLightColor nextColor = flank.getTrafficLightColor();
155                     if (TrafficLightColor.GREEN == nextColor)
156                     {
157                         greenCount++;
158                         if (greenCount > 1)
159                         {
160                             flanks.remove(index);
161                             index--;
162                             combined = true;
163                             continue;
164                         }
165                     }
166                     else if (TrafficLightColor.YELLOW == nextColor)
167                     {
168                         if (greenCount > 1)
169                         {
170                             flanks.remove(index);
171                             index--;
172                             continue;
173                         }
174                     }
175                     else if (TrafficLightColor.RED == nextColor)
176                     {
177                         greenCount--;
178                         if (greenCount > 0)
179                         {
180                             flanks.remove(index);
181                             index--;
182                             continue;
183                         }
184                     }
185                 }
186                 if (combined)
187                 {
188                     // Traffic light has adjacent or overlapping green realizations.
189                     String newSignalGroupName = "CombinedSignalGroups_";
190                     // Remove the traffic light from the current signal groups that it is part of
191                     for (SignalGroup sg : sgList)
192                     {
193                         // System.out.println("Reducing " + sg);
194                         newSignalGroupName = newSignalGroupName + "_" + sg.getId();
195                         Set<String> trafficLightIds = new LinkedHashSet<>();
196                         for (String tlId : sg.getTrafficLightIds())
197                         {
198                             if (!tlId.equals(trafficLightId))
199                             {
200                                 trafficLightIds.add(tlId);
201                             }
202                         }
203                         this.signalGroups.remove(sg);
204                         if (trafficLightIds.size() > 0)
205                         {
206                             SignalGroup newSignalGroup = new SignalGroup(sg.getId(), trafficLightIds, sg.getOffset(),
207                                     sg.getPreGreen(), sg.getGreen(), sg.getYellow());
208                             this.signalGroups.add(newSignalGroup);
209                         }
210                     }
211                     // Create new signal group(s) for each green realization of the traffic light
212                     Duration sgOffset = null;
213                     Duration preGreen = Duration.ZERO;
214                     Duration green = null;
215                     Duration yellow = Duration.ZERO;
216                     double cumulativeOffset = 0;
217                     for (int index = 0; index < flanks.size(); index++)
218                     {
219                         Flank flank = flanks.get(index);
220                         if (null == sgOffset)
221                         {
222                             sgOffset = Duration.instantiateSI(flank.getOffset());
223                         }
224                         if (TrafficLightColor.GREEN == flank.getTrafficLightColor())
225                         {
226                             preGreen = Duration.instantiateSI(flank.getOffset() - sgOffset.si);
227                         }
228                         if (TrafficLightColor.YELLOW == flank.getTrafficLightColor())
229                         {
230                             green = Duration.instantiateSI(flank.getOffset() - cumulativeOffset);
231                         }
232                         if (TrafficLightColor.RED == flank.getTrafficLightColor())
233                         {
234                             nextNumber++;
235                             yellow = Duration.instantiateSI(flank.getOffset() - cumulativeOffset);
236                             Set<String> trafficLightIds = new LinkedHashSet<>(1);
237                             trafficLightIds.add(trafficLightId);
238                             SignalGroup newSignalGroup = new SignalGroup(newSignalGroupName + "_" + nextNumber, trafficLightIds,
239                                     sgOffset, preGreen, green, yellow);
240                             this.signalGroups.add(newSignalGroup);
241                         }
242                         cumulativeOffset = flank.getOffset();
243                     }
244                 }
245             }
246         }
247     }
248 
249     /**
250      * Initiates all traffic control events.
251      * @param simulator OtsSimulatorInterface; simulator
252      * @param network Network; network
253      * @throws SimRuntimeException when traffic light does not exist in the network
254      */
255     @SuppressWarnings("unused")
256     private void setup(final OtsSimulatorInterface simulator, final Network network) throws SimRuntimeException
257     {
258         for (SignalGroup signalGroup : this.signalGroups)
259         {
260             signalGroup.startup(this.offset, this.cycleTime, simulator, network);
261         }
262     }
263 
264     /** {@inheritDoc} */
265     @Override
266     public void notify(final Event event) throws RemoteException
267     {
268         // nothing
269     }
270 
271     /** {@inheritDoc} */
272     @Override
273     public String getFullId()
274     {
275         return getId();
276     }
277 
278     /**
279      * @return cycleTime.
280      */
281     public final Duration getCycleTime()
282     {
283         return this.cycleTime;
284     }
285 
286     /**
287      * @return offset.
288      */
289     public final Duration getOffset()
290     {
291         return this.offset;
292     }
293 
294     /**
295      * @return signalGroups.
296      */
297     public final Set<SignalGroup> getSignalGroups()
298     {
299         return this.signalGroups;
300     }
301 
302     /** {@inheritDoc} */
303     @Override
304     public String toString()
305     {
306         return "FixedTimeController [cycleTime=" + this.cycleTime + ", offset=" + this.offset + ", signalGroups="
307                 + this.signalGroups + ", full id=" + this.getFullId() + "]";
308     }
309 
310     /**
311      * Fixed time signal group. A group of traffic lights who's colors change simultaneously.
312      * <p>
313      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
314      * <br>
315      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
316      * </p>
317      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
318      * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
319      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
320      */
321     public static class SignalGroup implements Identifiable
322     {
323 
324         /** Id. */
325         private final String id;
326 
327         /** Traffic light ids. */
328         private final ImmutableSet<String> trafficLightIds;
329 
330         /** Offset from start of cycle. */
331         private final Duration offset;
332 
333         /** Pre-green duration. */
334         private final Duration preGreen;
335 
336         /** Green duration. */
337         private final Duration green;
338 
339         /** Yellow duration. */
340         private final Duration yellow;
341 
342         /** Current color (according to <b>this</b> SignalGroup). */
343         private TrafficLightColor currentColor = TrafficLightColor.RED;
344 
345         // The following properties are remembered for the updates after the startup
346 
347         /** Traffic light objects. */
348         private List<TrafficLight> trafficLights;
349 
350         /** Simulator. */
351         private OtsSimulatorInterface simulator;
352 
353         /** Red time. */
354         private Duration red;
355 
356         /**
357          * Constructor without pre-green duration.
358          * @param id String; id
359          * @param trafficLightIds Set&lt;String&gt;; traffic light ids
360          * @param offset Duration; offset from start of cycle
361          * @param green Duration; green duration
362          * @param yellow Duration; yellow duration
363          */
364         public SignalGroup(final String id, final Set<String> trafficLightIds, final Duration offset, final Duration green,
365                 final Duration yellow)
366         {
367             this(id, trafficLightIds, offset, Duration.ZERO, green, yellow);
368         }
369 
370         /**
371          * Constructor with pre-green duration.
372          * @param id String; id
373          * @param trafficLightIds Set&lt;String&gt;; traffic light ids
374          * @param offset Duration; offset from start of cycle
375          * @param preGreen Duration; pre-green duration
376          * @param green Duration; green duration
377          * @param yellow Duration; yellow duration
378          */
379         public SignalGroup(final String id, final Set<String> trafficLightIds, final Duration offset, final Duration preGreen,
380                 final Duration green, final Duration yellow)
381         {
382             Throw.whenNull(id, "Id may not be null.");
383             Throw.whenNull(trafficLightIds, "Traffic light ids may not be null.");
384             Throw.whenNull(offset, "Offset may not be null.");
385             Throw.whenNull(preGreen, "Pre-green may not be null.");
386             Throw.when(preGreen.lt(Duration.ZERO), IllegalArgumentException.class, "Pre green duration may not be negative");
387             Throw.whenNull(green, "Green may not be null.");
388             Throw.when(green.lt(Duration.ZERO), IllegalArgumentException.class, "Green duration may not be negative");
389             Throw.whenNull(yellow, "Yellow may not be null.");
390             Throw.when(yellow.lt(Duration.ZERO), IllegalArgumentException.class, "Yellow duration may not be negative");
391             Throw.when(trafficLightIds.isEmpty(), IllegalArgumentException.class, "Traffic light ids may not be empty.");
392             this.id = id;
393             this.trafficLightIds = new ImmutableHashSet<>(trafficLightIds, Immutable.COPY);
394             this.offset = offset;
395             this.preGreen = preGreen;
396             this.green = green;
397             this.yellow = yellow;
398         }
399 
400         /**
401          * Retrieve the id of this signal group.
402          * @return String
403          */
404         @Override
405         public String getId()
406         {
407             return this.id;
408         }
409 
410         /**
411          * Connect to the traffic lights in the network, initialize the traffic lights to their initial color and schedule the
412          * first transitions.
413          * @param controllerOffset Duration;
414          * @param cycleTime Duration;
415          * @param theSimulator OtsSimulatorInterface;
416          * @param network Network;
417          * @throws SimRuntimeException when traffic light does not exist in the network
418          */
419         public void startup(final Duration controllerOffset, final Duration cycleTime, final OtsSimulatorInterface theSimulator,
420                 final Network network) throws SimRuntimeException
421         {
422             this.simulator = theSimulator;
423             double totalOffsetSI = this.offset.si + controllerOffset.si;
424             while (totalOffsetSI < 0.0)
425             {
426                 totalOffsetSI += cycleTime.si;
427             }
428             Duration totalOffset = Duration.instantiateSI(totalOffsetSI % cycleTime.si);
429             this.red = cycleTime.minus(this.preGreen).minus(this.green).minus(this.yellow);
430             Throw.when(this.red.lt0(), IllegalArgumentException.class, "Cycle time shorter than sum of non-red times.");
431 
432             this.trafficLights = new ArrayList<>();
433             ImmutableMap<String, TrafficLight> trafficLightObjects = network.getObjectMap(TrafficLight.class);
434             for (String trafficLightId : this.trafficLightIds)
435             {
436                 TrafficLight trafficLight = trafficLightObjects.get(trafficLightId);
437                 if (null == trafficLight) // Traffic light not found using id; try to find it by full id
438                 {
439                     // TODO: networkId.trafficLightId? Shouldn't that be linkId.trafficLightId?
440                     trafficLight = trafficLightObjects.get(network.getId() + "." + trafficLightId);
441                 }
442                 Throw.when(trafficLight == null, SimRuntimeException.class, "Traffic light \"" + trafficLightId
443                         + "\" in fixed time controller could not be found in network " + network.getId() + ".");
444                 this.trafficLights.add(trafficLight);
445             }
446             Duration inCycleTime = Duration.ZERO.minus(totalOffset);
447             while (inCycleTime.si < 0)
448             {
449                 inCycleTime = inCycleTime.plus(cycleTime);
450             }
451             Duration duration = null;
452             if (inCycleTime.ge(this.preGreen.plus(this.green).plus(this.yellow)))
453             {
454                 this.currentColor = TrafficLightColor.RED; // redundant; it is already RED
455                 duration = cycleTime.minus(inCycleTime);
456             }
457             else if (inCycleTime.lt(this.preGreen))
458             {
459                 this.currentColor = TrafficLightColor.PREGREEN;
460                 duration = this.preGreen.minus(inCycleTime);
461             }
462             else if (inCycleTime.lt(this.preGreen.plus(this.green)))
463             {
464                 this.currentColor = TrafficLightColor.GREEN;
465                 duration = this.preGreen.plus(this.green).minus(inCycleTime);
466             }
467             else if (inCycleTime.lt(this.preGreen.plus(this.green).plus(this.yellow)))
468             {
469                 this.currentColor = TrafficLightColor.YELLOW;
470                 duration = this.preGreen.plus(this.green).plus(this.yellow).minus(inCycleTime);
471             }
472             else
473             {
474                 throw new SimRuntimeException("Cannot determine initial state of signal group " + this);
475             }
476             setTrafficLights(this.currentColor);
477             this.simulator.scheduleEventRel(duration, this, "updateColors", null);
478         }
479 
480         /**
481          * Updates the color of the traffic lights.
482          */
483         @SuppressWarnings("unused")
484         private void updateColors()
485         {
486             try
487             {
488                 Duration duration = Duration.ZERO;
489                 TrafficLightColor color = this.currentColor;
490                 while (duration.le0())
491                 {
492                     switch (color)
493                     {
494                         case PREGREEN:
495                             color = TrafficLightColor.GREEN;
496                             duration = this.green;
497                             break;
498                         case GREEN:
499                             color = TrafficLightColor.YELLOW;
500                             duration = this.yellow;
501                             break;
502                         case YELLOW:
503                             color = TrafficLightColor.RED;
504                             duration = this.red;
505                             break;
506                         case RED:
507                             color = TrafficLightColor.PREGREEN;
508                             duration = this.preGreen;
509                             break;
510                         default:
511                             throw new RuntimeException("Cannot happen.");
512                     }
513                 }
514                 setTrafficLights(color);
515                 this.simulator.scheduleEventRel(duration, this, "updateColors", null);
516             }
517             catch (SimRuntimeException exception)
518             {
519                 // cannot happen; we check all durations for consistency
520                 throw new RuntimeException(exception);
521             }
522         }
523 
524         /**
525          * Change the color of our traffic lights.
526          * @param trafficLightColor TrafficLightColor; the new traffic light color to show
527          */
528         private void setTrafficLights(final TrafficLightColor trafficLightColor)
529         {
530             this.currentColor = trafficLightColor;
531             for (TrafficLight trafficLight : this.trafficLights)
532             {
533                 trafficLight.setTrafficLightColor(trafficLightColor);
534             }
535         }
536 
537         /** {@inheritDoc} */
538         @Override
539         public int hashCode()
540         {
541             final int prime = 31;
542             int result = 1;
543             result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
544             return result;
545         }
546 
547         /** {@inheritDoc} */
548         @Override
549         public boolean equals(final Object obj)
550         {
551             if (this == obj)
552             {
553                 return true;
554             }
555             if (obj == null)
556             {
557                 return false;
558             }
559             if (getClass() != obj.getClass())
560             {
561                 return false;
562             }
563             SignalGroup other = (SignalGroup) obj;
564             if (this.id == null)
565             {
566                 if (other.id != null)
567                 {
568                     return false;
569                 }
570             }
571             else if (!this.id.equals(other.id))
572             {
573                 return false;
574             }
575             return true;
576         }
577 
578         /**
579          * @return trafficLights.
580          */
581         public final ImmutableList<TrafficLight> getTrafficLights()
582         {
583             return new ImmutableArrayList<>(this.trafficLights);
584         }
585 
586         /**
587          * @return red.
588          */
589         public final Duration getRed()
590         {
591             return this.red;
592         }
593 
594         /**
595          * @return trafficLightIds.
596          */
597         public final ImmutableSet<String> getTrafficLightIds()
598         {
599             return this.trafficLightIds;
600         }
601 
602         /**
603          * @return offset.
604          */
605         public final Duration getOffset()
606         {
607             return this.offset;
608         }
609 
610         /**
611          * @return preGreen.
612          */
613         public final Duration getPreGreen()
614         {
615             return this.preGreen;
616         }
617 
618         /**
619          * @return green.
620          */
621         public final Duration getGreen()
622         {
623             return this.green;
624         }
625 
626         /**
627          * @return yellow.
628          */
629         public final Duration getYellow()
630         {
631             return this.yellow;
632         }
633 
634         /**
635          * Retrieve the current color of this SignalGroup.
636          * @return TrafficLightColor; the current color of this signal group.
637          */
638         public TrafficLightColor getCurrentColor()
639         {
640             return this.currentColor;
641         }
642 
643         /** {@inheritDoc} */
644         @Override
645         public String toString()
646         {
647             return "SignalGroup [id=" + this.id + ", trafficLightIds=" + this.trafficLightIds + ", offset=" + this.offset
648                     + ", preGreen=" + this.preGreen + ", green=" + this.green + ", yellow=" + this.yellow + "currentColor="
649                     + this.currentColor + "]";
650         }
651 
652     }
653 
654     /**
655      * Storage of an offset within a cycle and the new traffic light color. Used to sort the flanks. The term 'flank' refers to
656      * the 'side' of the shape of an electronic signal impulse.
657      */
658     class Flank implements Comparable<Flank>
659     {
660         /** When (in the cycle time is this transition. */
661         private final double offset;
662 
663         /** What is the color after this transition. */
664         private final TrafficLightColor newColor;
665 
666         /**
667          * Construct a new Flank.
668          * @param offset double; offset within the cycle time
669          * @param newColor TrafficLightColor; color to show after this transition
670          */
671         Flank(final double offset, final TrafficLightColor newColor)
672         {
673             this.offset = offset;
674             this.newColor = newColor;
675         }
676 
677         /**
678          * Retrieve the offset.
679          * @return double; the offset
680          */
681         public double getOffset()
682         {
683             return this.offset;
684         }
685 
686         /**
687          * Retrieve the color after this transition.
688          * @return TrafficLightColor; the color after this transition
689          */
690         public TrafficLightColor getTrafficLightColor()
691         {
692             return this.newColor;
693         }
694 
695         @Override
696         public String toString()
697         {
698             return "Flank [offset=" + this.offset + ", newColor=" + this.newColor + "]";
699         }
700 
701         /** Cumulative rounding errors are less than this value and traffic light transitions are spaced further apart. */
702         private static final double COMPARE_MARGIN = 0.01;
703 
704         @Override
705         public int compareTo(final Flank o)
706         {
707             double deltaOffset = this.offset - o.offset;
708             if (Math.abs(deltaOffset) < COMPARE_MARGIN)
709             {
710                 deltaOffset = 0;
711             }
712             if (deltaOffset > 0)
713             {
714                 return 1;
715             }
716             if (deltaOffset < 0)
717             {
718                 return -1;
719             }
720             if (TrafficLightColor.GREEN == this.newColor && TrafficLightColor.GREEN != o.newColor)
721             {
722                 return -1;
723             }
724             if (TrafficLightColor.GREEN == o.newColor && TrafficLightColor.GREEN != this.newColor)
725             {
726                 return 1;
727             }
728             return 0;
729         }
730 
731     }
732 
733 }