From f36a657dc295b6858ba390c668a4725f6c5c7ef9 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 29 Feb 2012 12:07:49 +0100 Subject: [PATCH] Added synchronization for MarkPassing collections, fixing bug 342 --- .../impl/SwissTimingRaceTrackerImpl.java | 11 ++- .../sailing/domain/tracking/TrackedRace.java | 10 ++- .../tracking/impl/DynamicTrackedRaceImpl.java | 33 +++++--- .../tracking/impl/RaceRankComparator.java | 51 ++++++------ .../impl/TrackedLegOfCompetitorImpl.java | 12 ++- .../domain/tracking/impl/TrackedRaceImpl.java | 64 +++++++++------ .../gwt/ui/server/SailingServiceImpl.java | 79 ++++++++++--------- 7 files changed, 159 insertions(+), 101 deletions(-) diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java index 0088a6a602f..9270a3acd67 100644 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java @@ -7,6 +7,7 @@ import java.text.ParseException; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.NavigableSet; import java.util.Set; import java.util.TreeMap; import java.util.logging.Logger; @@ -193,9 +194,13 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen // we need to use this to *update* the competitor's mark passings list, not *replace* it TreeMap markPassingsByMarkIndex = new TreeMap(); // now fill with the already existing mark passings for the competitor identified by boatID... - for (MarkPassing markPassing : trackedRace.getMarkPassings(competitor)) { - markPassingsByMarkIndex.put( - trackedRace.getRace().getCourse().getIndexOfWaypoint(markPassing.getWaypoint()), markPassing); + NavigableSet markPassings = trackedRace.getMarkPassings(competitor); + synchronized (markPassings) { + for (MarkPassing markPassing : markPassings) { + markPassingsByMarkIndex.put( + trackedRace.getRace().getCourse().getIndexOfWaypoint(markPassing.getWaypoint()), + markPassing); + } } // ...and then overwrite those for which we received "new evidence" for (Triple markIndexRankAndTimeSinceStartInMilliseconds : markIndicesRanksAndTimesSinceStartInMilliseconds) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index 2bb6505815f..126051235a7 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking; import java.util.List; import java.util.NavigableSet; +import java.util.SortedSet; import com.sap.sailing.domain.base.Buoy; import com.sap.sailing.domain.base.Competitor; @@ -148,6 +149,10 @@ public interface TrackedRace { * which point in time. This can, e.g., be used to sort those competitors who already finished a leg within the leg * that ends with waypoint. The remaining competitors need to be ordered by the advantage line-related * distance to the waypoint. + * + * @return the iterable sequence of {@link MarkPassing}s as described above. To iterate on the resulting collection + * the caller needs to synchronize on the iterable returned because insertions into the underlying + * collection will also synchronize on that collection. */ Iterable getMarkPassingsInOrder(Waypoint waypoint); @@ -199,7 +204,10 @@ public interface TrackedRace { * returned such that their {@link MarkPassing#getWaypoint() waypoints} are ordered in the same way they are ordered * in the race's {@link Course}. Note, that this doesn't necessarily guarantee ascending time points, particularly * if premature mark passings have been detected accidentally as can be the case with some tracking providers such - * as TracTrac. + * as TracTrac. If the caller wants to iterate on the resulting collection or construct a {@link SortedSet#headSet(Object)} + * or {@link SortedSet#tailSet(Object)} and then iterate over that, the caller needs to synchronize on the + * collection returned because insertions into the competitor's mark passing collection will also synchronize + * on that collection. */ NavigableSet getMarkPassings(Competitor competitor); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java index 6dcf1f40870..96e5618947b 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java @@ -240,15 +240,22 @@ public class DynamicTrackedRaceImpl extends TrackedRaceImpl implements public void updateMarkPassings(Competitor competitor, Iterable markPassings) { Map oldMarkPassings = new HashMap(); synchronized (this) { - for (MarkPassing oldMarkPassing : getMarkPassings(competitor)) { - oldMarkPassings.put(oldMarkPassing.getWaypoint(), oldMarkPassing); + NavigableSet markPassingsForCompetitor = getMarkPassings(competitor); + synchronized (markPassingsForCompetitor) { + for (MarkPassing oldMarkPassing : markPassingsForCompetitor) { + oldMarkPassings.put(oldMarkPassing.getWaypoint(), oldMarkPassing); + } } clearMarkPassings(competitor); - NavigableSet competitorMarkPassings = getMarkPassings(competitor); TimePoint timePointOfLatestEvent = new MillisecondsTimePoint(0); for (MarkPassing markPassing : markPassings) { - competitorMarkPassings.add(markPassing); - getMarkPassingsInOrder(markPassing.getWaypoint()).add(markPassing); + synchronized (markPassingsForCompetitor) { + markPassingsForCompetitor.add(markPassing); + } + Collection markPassingsInOrderForWaypoint = getMarkPassingsInOrder(markPassing.getWaypoint()); + synchronized (markPassingsInOrderForWaypoint) { + markPassingsInOrderForWaypoint.add(markPassing); + } if (markPassing.getTimePoint().compareTo(timePointOfLatestEvent) > 0) { timePointOfLatestEvent = markPassing.getTimePoint(); } @@ -267,11 +274,17 @@ public class DynamicTrackedRaceImpl extends TrackedRaceImpl implements } private void clearMarkPassings(Competitor competitor) { - Iterator mpIter = getMarkPassings(competitor).iterator(); - while (mpIter.hasNext()) { - MarkPassing mp = mpIter.next(); - mpIter.remove(); - getMarkPassingsInOrder(mp.getWaypoint()).remove(mp); + NavigableSet markPassings = getMarkPassings(competitor); + synchronized (markPassings) { + Iterator mpIter = markPassings.iterator(); + while (mpIter.hasNext()) { + MarkPassing mp = mpIter.next(); + mpIter.remove(); + Collection markPassingsInOrder = getMarkPassingsInOrder(mp.getWaypoint()); + synchronized (markPassingsInOrder) { + markPassingsInOrder.remove(mp); + } + } } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/RaceRankComparator.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/RaceRankComparator.java index 602e3dbdf61..fe89f5fc8bd 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/RaceRankComparator.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/RaceRankComparator.java @@ -43,28 +43,35 @@ public class RaceRankComparator implements Comparator { if (o1 == o2) { result = 0; } else { - // TODO see also bug 340/342; need to synchronize on TrackedRace to avoid concurrent updates to MarkPassings, although this is course-grained - NavigableSet o1MarkPassings = trackedRace.getMarkPassings(o1).headSet( - markPassingWithTimePoint, /* inclusive */true); - NavigableSet o2MarkPassings = trackedRace.getMarkPassings(o2).headSet( - markPassingWithTimePoint, /* inclusive */true); - result = o2MarkPassings.size() - o1MarkPassings.size(); // inverted: more legs means smaller rank - if (result == 0 && o1MarkPassings.size() > 0) { - // Competitors are on same leg and both have already started the first leg. - // TrackedLegOfCompetitor comparison also correctly uses finish times for a leg - // in case we have the final leg, so both competitors finished the race. - TrackedLegOfCompetitor o1Leg = trackedRace.getCurrentLeg(o1, timePoint); - if (o1Leg == null) { - // both must already finished race; sort by race finish time: earlier time means smaller (better) - // rank - result = o1MarkPassings.last().getTimePoint().compareTo(o2MarkPassings.last().getTimePoint()); - } else { - TrackedLegOfCompetitor o2Leg = trackedRace.getCurrentLeg(o2, timePoint); - if (o2Leg == null) { - result = 1; // o1Leg != null, so o1 has started leg already, o2 hasn't - } else { - result = new WindwardToGoComparator(trackedRace.getTrackedLeg(o1Leg.getLeg()), timePoint) - .compare(o1Leg, o2Leg); + NavigableSet o1MarkPassings = trackedRace.getMarkPassings(o1); + synchronized (o1MarkPassings) { + NavigableSet o2MarkPassings = trackedRace.getMarkPassings(o2); + synchronized (o2MarkPassings) { + NavigableSet o1MarkPassingsBeforeTimePoint = o1MarkPassings.headSet( + markPassingWithTimePoint, /* inclusive */true); + NavigableSet o2MarkPassingsBeforeTimePoint = o2MarkPassings.headSet( + markPassingWithTimePoint, /* inclusive */true); + result = o2MarkPassingsBeforeTimePoint.size() - o1MarkPassingsBeforeTimePoint.size(); // inverted: more legs means smaller rank + if (result == 0 && o1MarkPassingsBeforeTimePoint.size() > 0) { + // Competitors are on same leg and both have already started the first leg. + // TrackedLegOfCompetitor comparison also correctly uses finish times for a leg + // in case we have the final leg, so both competitors finished the race. + TrackedLegOfCompetitor o1Leg = trackedRace.getCurrentLeg(o1, timePoint); + if (o1Leg == null) { + // both must already finished race; sort by race finish time: earlier time means smaller + // (better) + // rank + result = o1MarkPassingsBeforeTimePoint.last().getTimePoint() + .compareTo(o2MarkPassingsBeforeTimePoint.last().getTimePoint()); + } else { + TrackedLegOfCompetitor o2Leg = trackedRace.getCurrentLeg(o2, timePoint); + if (o2Leg == null) { + result = 1; // o1Leg != null, so o1 has started leg already, o2 hasn't + } else { + result = new WindwardToGoComparator(trackedRace.getTrackedLeg(o1Leg.getLeg()), + timePoint).compare(o1Leg, o2Leg); + } + } } } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java index 0ad8027f563..0060af7da8e 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java @@ -374,13 +374,17 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { if (windwardSpeed == null) { return null; } else { - Iterator markPassingsForLegEnd = getTrackedRace().getMarkPassingsInOrder(getLeg().getTo()) - .iterator(); // Has our competitor started the leg already? If not, we won't be able to compute a gap if (hasStartedLeg(timePoint)) { - if (markPassingsForLegEnd.hasNext()) { + Iterable markPassingsInOrder = getTrackedRace().getMarkPassingsInOrder(getLeg().getTo()); + MarkPassing firstMarkPassing = null; + synchronized (markPassingsInOrder) { + Iterator markPassingsForLegEnd = markPassingsInOrder.iterator(); + firstMarkPassing = markPassingsForLegEnd.next(); + } + if (firstMarkPassing != null) { // someone has already finished the leg - TimePoint whenLeaderFinishedLeg = markPassingsForLegEnd.next().getTimePoint(); + TimePoint whenLeaderFinishedLeg = firstMarkPassing.getTimePoint(); // Was it before the requested timePoint? if (whenLeaderFinishedLeg.compareTo(timePoint) <= 0) { // Has our competitor also already finished this leg? diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index b40044f708a..c9affcfaa55 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -208,9 +208,15 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { @Override public TimePoint getStart() { TimePoint result; - Iterator markPassingsFirstMarkIter = getMarkPassingsInOrder(getRace().getCourse().getFirstWaypoint()).iterator(); - if (markPassingsFirstMarkIter.hasNext()) { - MarkPassing firstMarkPassingFirstMark = markPassingsFirstMarkIter.next(); + Iterable markPassingsInOrder = getMarkPassingsInOrder(getRace().getCourse().getFirstWaypoint()); + MarkPassing firstMarkPassingFirstMark = null; + synchronized (markPassingsInOrder) { + Iterator markPassingsFirstMarkIter = markPassingsInOrder.iterator(); + if (markPassingsFirstMarkIter.hasNext()) { + firstMarkPassingFirstMark = markPassingsFirstMarkIter.next(); + } + } + if (firstMarkPassingFirstMark != null) { TimePoint timeOfFirstMarkPassingFirstMark = firstMarkPassingFirstMark.getTimePoint(); if (startTimeReceived != null) { long startTimeReceived2timeOfFirstMarkPassingFirstMark = timeOfFirstMarkPassingFirstMark.asMillis() - @@ -232,8 +238,11 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { @Override public TimePoint getAssumedEnd() { TimePoint result = null; - for (MarkPassing passingFinishLine : getMarkPassingsInOrder(getRace().getCourse().getLastWaypoint())) { - result = passingFinishLine.getTimePoint(); + Iterable markPassingsInOrder = getMarkPassingsInOrder(getRace().getCourse().getLastWaypoint()); + synchronized (markPassingsInOrder) { + for (MarkPassing passingFinishLine : markPassingsInOrder) { + result = passingFinishLine.getTimePoint(); + } } return result; } @@ -756,19 +765,20 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { if (!track.hasDirectionChange(timePoint, getManeuverDegreeAngleThreshold())) { // reduce confidence around mark passings: NavigableSet markPassings = getMarkPassings(competitor); - NavigableSet prevMarkPassing = markPassings.headSet(dummyMarkPassingForNow, /* inclusive */ true); - NavigableSet nextMarkPassing = markPassings.tailSet(dummyMarkPassingForNow, /* inclusive */ true); double markPassingProximityConfidenceReduction = 1.0; - if (prevMarkPassing != null && !prevMarkPassing.isEmpty()) { - markPassingProximityConfidenceReduction *= Math.max(0.0, - 1.0-weigherForMarkPassingProximity.getConfidence(prevMarkPassing.last().getTimePoint(), timePoint)); + synchronized (markPassings) { + NavigableSet prevMarkPassing = markPassings.headSet(dummyMarkPassingForNow, /* inclusive */ true); + NavigableSet nextMarkPassing = markPassings.tailSet(dummyMarkPassingForNow, /* inclusive */ true); + if (prevMarkPassing != null && !prevMarkPassing.isEmpty()) { + markPassingProximityConfidenceReduction *= Math.max(0.0, + 1.0-weigherForMarkPassingProximity.getConfidence(prevMarkPassing.last().getTimePoint(), timePoint)); + } + if (nextMarkPassing != null && !nextMarkPassing.isEmpty()) { + markPassingProximityConfidenceReduction *= Math.max(0.0, + 1.0-weigherForMarkPassingProximity.getConfidence(nextMarkPassing.first().getTimePoint(), timePoint)); + } } - if (nextMarkPassing != null && !nextMarkPassing.isEmpty()) { - markPassingProximityConfidenceReduction *= Math.max(0.0, - 1.0-weigherForMarkPassingProximity.getConfidence(nextMarkPassing.first().getTimePoint(), timePoint)); - } - SpeedWithBearingWithConfidence estimatedSpeedWithConfidence = track.getEstimatedSpeed(timePoint, - weigher); + SpeedWithBearingWithConfidence estimatedSpeedWithConfidence = track.getEstimatedSpeed(timePoint, weigher); if (estimatedSpeedWithConfidence != null) { BearingWithConfidence bearing = new BearingWithConfidenceImpl( estimatedSpeedWithConfidence.getObject() == null ? null : estimatedSpeedWithConfidence.getObject().getBearing(), @@ -1117,13 +1127,18 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { Placemark startBest = null; Placemark finishBest = null; - //Get start postition + // Get start postition Waypoint start = getRace().getCourse().getFirstWaypoint(); Iterable startPassings = getMarkPassingsInOrder(start); - if (startPassings.iterator().hasNext()) { - MarkPassing startPassing = startPassings.iterator().next(); + MarkPassing startPassing = null; + synchronized (startPassings) { + Iterator iterator = startPassings.iterator(); + if (iterator.hasNext()) { + startPassing = iterator.next(); + } + } + if (startPassing != null) { Position startPosition = getApproximatePosition(start, startPassing.getTimePoint()); - try { // Get distance to nearest placemark and calculate the search radius Placemark startNearest = ReverseGeocoder.INSTANCE.getPlacemarkNearest(startPosition); @@ -1142,13 +1157,14 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { // Get finish position Waypoint finish = getRace().getCourse().getLastWaypoint(); Iterable finishPassings = getMarkPassingsInOrder(finish); - Iterator finishPassingsIterator = finishPassings.iterator(); MarkPassing finishPassing = null; - while (finishPassingsIterator.hasNext()) { - finishPassing = (MarkPassing) finishPassingsIterator.next(); + synchronized (finishPassings) { + Iterator finishPassingsIterator = finishPassings.iterator(); + while (finishPassingsIterator.hasNext()) { + finishPassing = (MarkPassing) finishPassingsIterator.next(); + } } Position finishPosition = getApproximatePosition(finish, finishPassing.getTimePoint()); - if (startPosition.getDistance(finishPosition).getKilometers() > ReverseGeocoder.POSITION_CACHE_DISTANCE_LIMIT_IN_KM) { try { // Get distance to nearest placemark and calculate the search radius diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index a06e901b268..2eb1452ce37 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -769,32 +769,35 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS // try to calculate the point in time where all boats passed the start line in a small time frame Waypoint from = trackedLeg.getLeg().getFrom(); Iterable markPassings = trackedRace.getMarkPassingsInOrder(from); - int markPassingsCount = Util.size(markPassings); - // require more than half the boats registered for the race to have passed the start mark - if (markPassings != null && markPassingsCount > Util.size(trackedRace.getRace().getCompetitors())/2) { - long maxTimeFrameInMs = 60 * 1000; // = 1 minute - Iterator iterator = markPassings.iterator(); - long currentTimeToCheck = 0; - int enoughCompetitors = markPassingsCount / 2; - int currentCompetitorsInTime = 0; - - while (iterator.hasNext()) { - MarkPassing currentMarkPassing = iterator.next(); - long diff = currentMarkPassing.getTimePoint().asMillis() - currentTimeToCheck; - if (diff > maxTimeFrameInMs) { - // reset the check - currentCompetitorsInTime = 0; - currentTimeToCheck = currentMarkPassing.getTimePoint().asMillis(); - } else { - currentCompetitorsInTime++; - if(currentCompetitorsInTime == enoughCompetitors) { - Date passingDate = currentMarkPassing.getTimePoint().asDate(); - LegTimesInfoDTO legTimepointDTO = new LegTimesInfoDTO("S"); - legTimepointDTO.firstPassingDate = passingDate; - legTimes.add(legTimepointDTO); - raceTimesInfo.setStartOfRace(passingDate); - lastLegPassingTime = passingDate; - break; + synchronized (markPassings) { + int markPassingsCount = Util.size(markPassings); + // require more than half the boats registered for the race to have passed the start mark + if (markPassings != null + && markPassingsCount > Util.size(trackedRace.getRace().getCompetitors()) / 2) { + long maxTimeFrameInMs = 60 * 1000; // = 1 minute + Iterator iterator = markPassings.iterator(); + long currentTimeToCheck = 0; + int enoughCompetitors = markPassingsCount / 2; + int currentCompetitorsInTime = 0; + + while (iterator.hasNext()) { + MarkPassing currentMarkPassing = iterator.next(); + long diff = currentMarkPassing.getTimePoint().asMillis() - currentTimeToCheck; + if (diff > maxTimeFrameInMs) { + // reset the check + currentCompetitorsInTime = 0; + currentTimeToCheck = currentMarkPassing.getTimePoint().asMillis(); + } else { + currentCompetitorsInTime++; + if (currentCompetitorsInTime == enoughCompetitors) { + Date passingDate = currentMarkPassing.getTimePoint().asDate(); + LegTimesInfoDTO legTimepointDTO = new LegTimesInfoDTO("S"); + legTimepointDTO.firstPassingDate = passingDate; + legTimes.add(legTimepointDTO); + raceTimesInfo.setStartOfRace(passingDate); + lastLegPassingTime = passingDate; + break; + } } } } @@ -807,17 +810,19 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS // ensure the passing date is in the right time order; there may perhaps be left-overs for marks to be reached later that // claim it has been passed in the past which may have been an accidental tracker read-out; // the results of getMarkPassingsInOrder(to) has by definition an ascending time-point ordering - for (MarkPassing currentMarkPassing : markPassings) { - Date currentPassingDate = currentMarkPassing.getTimePoint().asDate(); - if (lastLegPassingTime == null) { - lastLegPassingTime = currentPassingDate; - } - if (currentPassingDate.after(lastLegPassingTime)) { - LegTimesInfoDTO legTimepointDTO = new LegTimesInfoDTO("L" + i++); - legTimepointDTO.firstPassingDate = currentPassingDate; - legTimes.add(legTimepointDTO); - lastLegPassingTime = currentPassingDate; - break; + synchronized (markPassings) { + for (MarkPassing currentMarkPassing : markPassings) { + Date currentPassingDate = currentMarkPassing.getTimePoint().asDate(); + if (lastLegPassingTime == null) { + lastLegPassingTime = currentPassingDate; + } + if (currentPassingDate.after(lastLegPassingTime)) { + LegTimesInfoDTO legTimepointDTO = new LegTimesInfoDTO("L" + i++); + legTimepointDTO.firstPassingDate = currentPassingDate; + legTimes.add(legTimepointDTO); + lastLegPassingTime = currentPassingDate; + break; + } } } }