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