mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-04 19:33:52 +00:00
Merge branch 'master' into test
Conflicts: java/pom.xml
This commit is contained in:
commit
326c3f72ba
52 files changed
+899
-1663
No files matched your search
+1
-1
@@ -55,7 +55,7 @@ public class DeclinationImportTest extends AbstractDeclinationTest {
|
||||
public void readOnlineOrFromFile() throws IOException, ClassNotFoundException, ParseException {
|
||||
Declination declination = importer.getDeclination(new DegreePosition(53, 3),
|
||||
new MillisecondsTimePoint(simpleDateFormat.parse("2011-05-27").getTime()),
|
||||
/* timeoutForOnlineFetchInMilliseconds */ 5000);
|
||||
/* timeoutForOnlineFetchInMilliseconds */ 10000);
|
||||
assertNotNull(declination);
|
||||
System.out.println(declination);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#Fri Jun 10 21:39:55 CEST 2011
|
||||
eclipse.preferences.version=1
|
||||
encoding//src/com/sap/sailing/domain/test/WindTest.java=UTF-8
|
||||
+10
-2
@@ -67,7 +67,7 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest {
|
||||
super.setUp();
|
||||
domainFactory = new DomainFactoryImpl();
|
||||
domainEvent = domainFactory.createEvent(getEvent());
|
||||
trackedEvent = domainFactory.trackEvent(domainEvent);
|
||||
trackedEvent = domainFactory.getOrCreateTrackedEvent(domainEvent);
|
||||
receivers.add(new RaceCourseReceiver(domainFactory, trackedEvent, getEvent(), /* millisecondsOverWhichToAverageWind */
|
||||
EmptyWindStore.INSTANCE,
|
||||
30000, /* millisecondsOverWhichToAverageSpeed */30000) {
|
||||
@@ -81,7 +81,10 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest {
|
||||
}
|
||||
});
|
||||
addListenersForStoredDataAndStartController(receivers);
|
||||
race = domainFactory.getRaceDefinition(getEvent().getRaceList().iterator().next());
|
||||
Race tractracRace = getEvent().getRaceList().iterator().next();
|
||||
// now we expect that there is no
|
||||
assertNull(domainFactory.getExistingRaceDefinitionForRace(tractracRace));
|
||||
race = domainFactory.getRaceDefinition(tractracRace);
|
||||
course = race.getCourse();
|
||||
assertNotNull(course);
|
||||
assertEquals(3, Util.size(course.getWaypoints()));
|
||||
@@ -91,6 +94,11 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest {
|
||||
assertEquals("top", course.getLegs().get(1).getFrom().getName());
|
||||
}
|
||||
|
||||
private void assertNull(RaceDefinition existingRaceDefinitionForRace) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the race course's legs have corresponding {@link TrackedLeg}s and the {@link TrackedLeg}s have
|
||||
* {@link TrackedLegOfCompetitor} for each of the race's competitors.
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ public class ReceiveMarkPassingDataTest extends AbstractTracTracLiveTest {
|
||||
List<Receiver> receivers = new ArrayList<Receiver>();
|
||||
receivers.add(receiver);
|
||||
for (Receiver r : DomainFactory.INSTANCE.getUpdateReceivers(
|
||||
DomainFactory.INSTANCE.trackEvent(DomainFactory.INSTANCE.createEvent(getEvent())),
|
||||
DomainFactory.INSTANCE.getOrCreateTrackedEvent(DomainFactory.INSTANCE.createEvent(getEvent())),
|
||||
getEvent(), EmptyWindStore.INSTANCE, ReceiverType.RACECOURSE,
|
||||
ReceiverType.MARKPOSITIONS, ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS)) {
|
||||
receivers.add(r);
|
||||
|
||||
+4
-1
@@ -72,13 +72,16 @@ public class ReceiveTrackingDataTest extends AbstractTracTracLiveTest {
|
||||
};
|
||||
List<TypeController> listeners = new ArrayList<TypeController>();
|
||||
Event event = domainFactory.createEvent(getEvent());
|
||||
DynamicTrackedEvent trackedEvent = domainFactory.trackEvent(event);
|
||||
DynamicTrackedEvent trackedEvent = domainFactory.getOrCreateTrackedEvent(event);
|
||||
trackedEvent.addRaceListener(new RaceListener() {
|
||||
@Override
|
||||
public void raceAdded(TrackedRace trackedRace) {
|
||||
System.out.println("Subscribing raw position listener for race "+trackedRace);
|
||||
((DynamicTrackedRace) trackedRace).addListener(positionListener);
|
||||
}
|
||||
@Override
|
||||
public void raceRemoved(TrackedRace trackedRace) {
|
||||
}
|
||||
});
|
||||
for (Receiver receiver : domainFactory.getUpdateReceivers(trackedEvent, getEvent(), EmptyWindStore.INSTANCE)) {
|
||||
for (TypeController raceListener : receiver.getTypeControllers()) {
|
||||
|
||||
@@ -224,4 +224,15 @@ public class TrackTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDistanceTraveledOnInBetweenSectionFromFixToFix() {
|
||||
// take second and third fix and compute distance between them
|
||||
Iterator<GPSFixMoving> iter = track.getFixes().iterator();
|
||||
iter.next(); // skip first;
|
||||
GPSFix second = iter.next();
|
||||
GPSFix third = iter.next();
|
||||
assertEquals(second.getPosition().getDistance(third.getPosition()),
|
||||
track.getDistanceTraveled(second.getTimePoint(), third.getTimePoint()));
|
||||
}
|
||||
}
|
||||
+17
-2
@@ -76,7 +76,11 @@ public interface DomainFactory {
|
||||
RaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, WindStore windStore) throws MalformedURLException,
|
||||
FileNotFoundException, URISyntaxException;
|
||||
|
||||
DynamicTrackedEvent trackEvent(com.sap.sailing.domain.base.Event event);
|
||||
/**
|
||||
* Looks for tracking information about <code>event</code>. If no such object exists yet, a new one
|
||||
* is created.
|
||||
*/
|
||||
DynamicTrackedEvent getOrCreateTrackedEvent(com.sap.sailing.domain.base.Event event);
|
||||
|
||||
BoatClass getBoatClass(CompetitorClass competitorClass);
|
||||
|
||||
@@ -93,7 +97,7 @@ public interface DomainFactory {
|
||||
*
|
||||
* @param trackedEvent
|
||||
* must have been created before through
|
||||
* {@link #trackEvent(com.sap.sailing.domain.base.Event)} because
|
||||
* {@link #getOrCreateTrackedEvent(com.sap.sailing.domain.base.Event)} because
|
||||
* otherwise the link to the {@link Event} can't be established
|
||||
* @param windStore TODO
|
||||
*/
|
||||
@@ -117,6 +121,11 @@ public interface DomainFactory {
|
||||
DynamicTrackedRace trackRace(TrackedEvent trackedEvent, RaceDefinition raceDefinition,
|
||||
WindStore windStore, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, Event tractracEvent);
|
||||
|
||||
/**
|
||||
* Non-blocking call that returns <code>null</code> if the {@link RaceDefinition} for the TracTrac Event
|
||||
* hasn't been created yet, e.g., because the course definition hasn't been received yet or the listener
|
||||
* for receiving course information hasn't been registered (yet).
|
||||
*/
|
||||
RaceDefinition getRace(Event tractracEvent);
|
||||
|
||||
JSONService parseJSONURL(URL jsonURL) throws IOException, ParseException, org.json.simple.parser.ParseException;
|
||||
@@ -133,4 +142,10 @@ public interface DomainFactory {
|
||||
* in a course. A single control point may be used more than once in a course's list of waypoints.
|
||||
*/
|
||||
void updateCourseWaypoints(Course courseToUpdate, List<ControlPoint> controlPoints) throws PatchFailedException;
|
||||
|
||||
/**
|
||||
* Looks for the tracking information for <code>event</code>. If not found, <code>null</code> is returned
|
||||
* immediately. See also {@link #getOrCreateTrackedEvent(com.sap.sailing.domain.base.Event)}.
|
||||
*/
|
||||
DynamicTrackedEvent getTrackedEvent(com.sap.sailing.domain.base.Event event);
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.sap.sailing.domain.tractracadapter;
|
||||
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedEvent;
|
||||
import com.sap.sailing.domain.tractracadapter.impl.RaceCourseReceiver;
|
||||
|
||||
/**
|
||||
* Gives clients a way to refer to a specific race even though the {@link RaceDefinition} object may not have
|
||||
* been created yet, but without exposing the TracTrac internals. The {@link #getRace()} call will block
|
||||
* until the {@link RaceDefinition} has been created.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface RaceHandle {
|
||||
|
||||
Event getEvent();
|
||||
|
||||
/**
|
||||
* Fetch the race definition. If the race definition represented by this handle hasn't been created yet, the call
|
||||
* blocks until such a definition is provided by another call, usually by the {@link RaceCourseReceiver}.
|
||||
*/
|
||||
RaceDefinition getRace();
|
||||
|
||||
DynamicTrackedEvent getTrackedEvent();
|
||||
|
||||
/**
|
||||
* The tracker managing the tracking of the race to which this is a handle.
|
||||
*/
|
||||
RaceTracker getRaceTracker();
|
||||
|
||||
}
|
||||
+24
@@ -2,15 +2,39 @@ package com.sap.sailing.domain.tractracadapter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedEvent;
|
||||
import com.sap.sailing.domain.tracking.TrackedEvent;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.util.Util.Triple;
|
||||
|
||||
public interface RaceTracker {
|
||||
|
||||
/**
|
||||
* Stops tracking the race and removes the {@link TrackedRace} object one gets from calling
|
||||
* {@link #getTrackedEvent()}.{@link TrackedEvent#getTrackedRace(RaceDefinition) getTrackedRace(}{@link #getRace() getRace())}
|
||||
* from the {@link #getTrackedEvent() tracked event}.
|
||||
*/
|
||||
void stop() throws MalformedURLException, IOException, InterruptedException;
|
||||
|
||||
com.sap.sailing.domain.base.Event getEvent();
|
||||
|
||||
/**
|
||||
* Non-blocking call that returns <code>null</code> if the {@link RaceDefinition} for the TracTrac Event
|
||||
* hasn't been created yet, e.g., because the course definition hasn't been received yet or the listener
|
||||
* for receiving course information hasn't been registered (yet).
|
||||
*/
|
||||
RaceDefinition getRace();
|
||||
|
||||
RaceHandle getRaceHandle();
|
||||
|
||||
DynamicTrackedEvent getTrackedEvent();
|
||||
|
||||
WindStore getWindStore();
|
||||
|
||||
Triple<URL, URI, URI> getURLs();
|
||||
|
||||
}
|
||||
+7
-2
@@ -74,7 +74,7 @@ import difflib.PatchFailedException;
|
||||
public class DomainFactoryImpl implements DomainFactory {
|
||||
private static final Logger logger = Logger.getLogger(DomainFactoryImpl.class.getName());
|
||||
|
||||
private final long millisecondsOverWhichToAverageSpeed = 5000;
|
||||
private final long millisecondsOverWhichToAverageSpeed = 15000;
|
||||
|
||||
// TODO clarify how millisecondsOverWhichToAverageWind could be updated and propagated live
|
||||
private final long millisecondsOverWhichToAverageWind = 30000;
|
||||
@@ -338,9 +338,14 @@ public class DomainFactoryImpl implements DomainFactory {
|
||||
ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACESTARTFINISH,
|
||||
ReceiverType.RAWPOSITIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicTrackedEvent getTrackedEvent(com.sap.sailing.domain.base.Event event) {
|
||||
return eventTrackingCache.get(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicTrackedEvent trackEvent(com.sap.sailing.domain.base.Event event) {
|
||||
public DynamicTrackedEvent getOrCreateTrackedEvent(com.sap.sailing.domain.base.Event event) {
|
||||
synchronized (eventTrackingCache) {
|
||||
DynamicTrackedEvent result = eventTrackingCache.get(event);
|
||||
if (result == null) {
|
||||
|
||||
+11
-4
@@ -84,19 +84,26 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<Route, RouteDa
|
||||
// Therefore, don't create TrackedRace again because it already exists.
|
||||
try {
|
||||
getDomainFactory().updateCourseWaypoints(course, event.getB().getPoints());
|
||||
if (trackedEvent.getExistingTrackedRace(existingRaceDefinitionForRace) == null) {
|
||||
createTrackedRace(existingRaceDefinitionForRace);
|
||||
}
|
||||
} catch (PatchFailedException e) {
|
||||
logger.log(Level.SEVERE, "Internal error updating race course "+course+": "+e.getMessage());
|
||||
logger.throwing(RaceCourseReceiver.class.getName(), "handleEvent", e);
|
||||
}
|
||||
} else {
|
||||
logger.log(Level.INFO, "Received course for non-existing race "+event.getC().getName()+". Creating RaceDefinition.");
|
||||
// create race redefinition
|
||||
// create race definition
|
||||
RaceDefinition raceDefinition = getDomainFactory().createRaceDefinition(event.getC(), course);
|
||||
trackedEvent.getEvent().addRace(raceDefinition);
|
||||
DynamicTrackedRace trackedRace = getDomainFactory().trackRace(trackedEvent, raceDefinition,
|
||||
windStore, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed, tractracEvent);
|
||||
trackedEvent.addTrackedRace(trackedRace);
|
||||
createTrackedRace(raceDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
private void createTrackedRace(RaceDefinition race) {
|
||||
DynamicTrackedRace trackedRace = getDomainFactory().trackRace(trackedEvent, race,
|
||||
windStore, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed, tractracEvent);
|
||||
trackedEvent.addTrackedRace(trackedRace);
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.sap.sailing.domain.tractracadapter.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedEvent;
|
||||
import com.sap.sailing.domain.tractracadapter.DomainFactory;
|
||||
import com.sap.sailing.domain.tractracadapter.RaceHandle;
|
||||
import com.sap.sailing.domain.tractracadapter.RaceTracker;
|
||||
import com.tractrac.clientmodule.Event;
|
||||
|
||||
public class RaceHandleImpl implements RaceHandle {
|
||||
private final Event tractracEvent;
|
||||
private final DomainFactory domainFactory;
|
||||
private final DynamicTrackedEvent trackedEvent;
|
||||
private final RaceTracker raceTracker;
|
||||
|
||||
public RaceHandleImpl(DomainFactory domainFactory, Event tractracEvent, DynamicTrackedEvent trackedEvent, RaceTracker raceTracker) {
|
||||
this.domainFactory = domainFactory;
|
||||
this.tractracEvent = tractracEvent;
|
||||
this.trackedEvent = trackedEvent;
|
||||
this.raceTracker = raceTracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.sap.sailing.domain.base.Event getEvent() {
|
||||
return domainFactory.createEvent(tractracEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceDefinition getRace() {
|
||||
// we assume there is exactly one Race per TracTrac event
|
||||
return domainFactory.getRaceDefinition(tractracEvent.getRaceList().iterator().next());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicTrackedEvent getTrackedEvent() {
|
||||
return trackedEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceTracker getRaceTracker() {
|
||||
return raceTracker;
|
||||
}
|
||||
|
||||
}
|
||||
+42
-10
@@ -16,10 +16,13 @@ import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedEvent;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tractracadapter.DomainFactory;
|
||||
import com.sap.sailing.domain.tractracadapter.RaceHandle;
|
||||
import com.sap.sailing.domain.tractracadapter.RaceTracker;
|
||||
import com.sap.sailing.domain.tractracadapter.Receiver;
|
||||
import com.sap.sailing.util.Util.Triple;
|
||||
import com.tractrac.clientmodule.Event;
|
||||
import com.tractrac.clientmodule.data.DataController;
|
||||
import com.tractrac.clientmodule.data.DataController.Listener;
|
||||
@@ -31,10 +34,18 @@ public class RaceTrackerImpl implements Listener, RaceTracker {
|
||||
private final Thread ioThread;
|
||||
private final DataController controller;
|
||||
private final Set<Receiver> receivers;
|
||||
private final DomainFactory domainFactory;
|
||||
private final DynamicTrackedEvent trackedEvent;
|
||||
private final WindStore windStore;
|
||||
private final Triple<URL, URI, URI> urls;
|
||||
|
||||
/**
|
||||
* Creates a race tracked for the specified URL/URIs and starts receiving all available existing and future push
|
||||
* data from there. Receiving continues until {@link #stop()} is called.
|
||||
* <p>
|
||||
*
|
||||
* A race tracker uses the <code>paramURL</code> for the TracTrac Java client to register for push data about one
|
||||
* race. The {@link RaceDefinition} for that race, however, cannot be created until the {@link Course} has been
|
||||
* race. The {@link RaceDefinition} for that race, however, isn't created until the {@link Course} has been
|
||||
* received. Therefore, the {@link RaceCourseReceiver} will create the {@link RaceDefinition} and will add it to the
|
||||
* {@link com.sap.sailing.domain.base.Event}.
|
||||
* <p>
|
||||
@@ -45,16 +56,16 @@ public class RaceTrackerImpl implements Listener, RaceTracker {
|
||||
* {@link Event} as argument that is used for its tracking.
|
||||
* <p>
|
||||
*
|
||||
* When {@link #getRace} is called on this object before the {@link RaceCourseReceiver} has created the
|
||||
* {@link RaceDefinition}, the call will block until this has happened.
|
||||
*
|
||||
* @param windStore
|
||||
* Provides the capability to obtain the {@link WindTrack}s for the different wind sources.
|
||||
* A trivial implementation is {@link EmptyWindStore} which simply provides new, empty tracks.
|
||||
* This is always available but loses track of the wind, e.g., during server restarts.
|
||||
* Provides the capability to obtain the {@link WindTrack}s for the different wind sources. A trivial
|
||||
* implementation is {@link EmptyWindStore} which simply provides new, empty tracks. This is always
|
||||
* available but loses track of the wind, e.g., during server restarts.
|
||||
*/
|
||||
protected RaceTrackerImpl(DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI, WindStore windStore)
|
||||
throws URISyntaxException, MalformedURLException, FileNotFoundException {
|
||||
urls = new Triple<URL, URI, URI>(paramURL, liveURI, storedURI);
|
||||
this.windStore = windStore;
|
||||
this.domainFactory = domainFactory;
|
||||
// Read event data from configuration file
|
||||
tractracEvent = KeyValue.setup(paramURL);
|
||||
|
||||
@@ -69,7 +80,7 @@ public class RaceTrackerImpl implements Listener, RaceTracker {
|
||||
// Start live and stored data streams
|
||||
ioThread = new Thread(controller, "io");
|
||||
domainEvent = domainFactory.createEvent(tractracEvent);
|
||||
DynamicTrackedEvent trackedEvent = domainFactory.trackEvent(domainEvent);
|
||||
trackedEvent = domainFactory.getOrCreateTrackedEvent(domainEvent);
|
||||
receivers = new HashSet<Receiver>();
|
||||
Set<TypeController> typeControllers = new HashSet<TypeController>();
|
||||
for (Receiver receiver : domainFactory.getUpdateReceivers(trackedEvent, tractracEvent, windStore)) {
|
||||
@@ -81,9 +92,29 @@ public class RaceTrackerImpl implements Listener, RaceTracker {
|
||||
addListenersForStoredDataAndStartController(typeControllers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Triple<URL, URI, URI> getURLs() {
|
||||
return urls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindStore getWindStore() {
|
||||
return windStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicTrackedEvent getTrackedEvent() {
|
||||
return trackedEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceHandle getRaceHandle() {
|
||||
return new RaceHandleImpl(domainFactory, tractracEvent, getTrackedEvent(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceDefinition getRace() {
|
||||
return DomainFactory.INSTANCE.getRace(tractracEvent);
|
||||
return domainFactory.getRace(tractracEvent);
|
||||
}
|
||||
|
||||
protected void addListenersForStoredDataAndStartController(Iterable<TypeController> listenersForStoredData) {
|
||||
@@ -118,7 +149,8 @@ public class RaceTrackerImpl implements Listener, RaceTracker {
|
||||
for (Receiver receiver : receivers) {
|
||||
receiver.stop();
|
||||
}
|
||||
ioThread.join();
|
||||
ioThread.join(3000); // wait no more than three seconds
|
||||
trackedEvent.removedTrackedRace(trackedEvent.getTrackedRace(getRace()));
|
||||
}
|
||||
|
||||
protected DataController getController() {
|
||||
|
||||
+10
@@ -7,6 +7,7 @@ import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.Mile;
|
||||
import com.sap.sailing.domain.base.Position;
|
||||
import com.sap.sailing.domain.base.Speed;
|
||||
import com.sap.sailing.domain.base.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.base.TimePoint;
|
||||
import com.sap.sailing.domain.base.impl.KilometersPerHourSpeedImpl;
|
||||
import com.sap.sailing.domain.base.impl.KnotSpeedImpl;
|
||||
@@ -139,4 +140,13 @@ public class TracTracTrackedLegOfCompetitor implements TrackedLegOfCompetitor {
|
||||
return trackedRace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getSpeedOverGround(TimePoint at) {
|
||||
if (hasStartedLeg(at)) {
|
||||
return getTrackedRace().getTrack(getCompetitor()).getEstimatedSpeed(at);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -6,6 +6,16 @@ import com.sap.sailing.domain.base.Speed;
|
||||
|
||||
public abstract class AbstractDistance implements Distance {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof Distance) && this.compareTo((Distance) o) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 9128347 ^ (int) getMeters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Distance o) {
|
||||
return getMeters() > o.getMeters() ? 1 : getMeters() == o.getMeters() ? 0 : -1;
|
||||
|
||||
+2
@@ -3,5 +3,7 @@ package com.sap.sailing.domain.tracking;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
|
||||
public interface DynamicTrackedEvent extends TrackedEvent {
|
||||
|
||||
DynamicTrackedRace getTrackedRace(RaceDefinition race);
|
||||
|
||||
}
|
||||
@@ -2,4 +2,6 @@ package com.sap.sailing.domain.tracking;
|
||||
|
||||
public interface RaceListener {
|
||||
void raceAdded(TrackedRace trackedRace);
|
||||
|
||||
void raceRemoved(TrackedRace trackedRace);
|
||||
}
|
||||
@@ -19,8 +19,16 @@ public interface TrackedEvent {
|
||||
*/
|
||||
TrackedRace getTrackedRace(RaceDefinition race);
|
||||
|
||||
/**
|
||||
* Non-blocking call that returns <code>null</code> if no tracking information currently exists
|
||||
* for <code>race</code>. See also {@link #getTrackedRace(RaceDefinition)} for a blocking variant.
|
||||
*/
|
||||
TrackedRace getExistingTrackedRace(RaceDefinition race);
|
||||
|
||||
void addTrackedRace(TrackedRace trackedRace);
|
||||
|
||||
void removedTrackedRace(TrackedRace trackedRace);
|
||||
|
||||
/**
|
||||
* Listener will be notified when {@link #addTrackedRace(TrackedRace)} is called and
|
||||
* upon registration for each tracked race already known. Therefore, the listener
|
||||
@@ -31,5 +39,5 @@ public interface TrackedEvent {
|
||||
int getTotalPoints(Competitor competitor, TimePoint timePoint);
|
||||
|
||||
int getNetPoints(Competitor competitor, TimePoint timePoint) throws NoWindException;
|
||||
|
||||
|
||||
}
|
||||
+3
@@ -4,6 +4,7 @@ import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Distance;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.Speed;
|
||||
import com.sap.sailing.domain.base.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.base.TimePoint;
|
||||
|
||||
public interface TrackedLegOfCompetitor {
|
||||
@@ -91,4 +92,6 @@ public interface TrackedLegOfCompetitor {
|
||||
*/
|
||||
Double getEstimatedTimeToNextMarkInSeconds(TimePoint timePoint) throws NoWindException;
|
||||
|
||||
SpeedWithBearing getSpeedOverGround(TimePoint at);
|
||||
|
||||
}
|
||||
@@ -137,8 +137,7 @@ public interface TrackedRace {
|
||||
WindTrack getWindTrack(WindSource windSource);
|
||||
|
||||
/**
|
||||
* Waits until {@link #getUpdateCount()} is after <code>since</code>.
|
||||
* @param sinceUpdate TODO
|
||||
* Waits until {@link #getUpdateCount()} is after <code>sinceUpdate</code>.
|
||||
*/
|
||||
void waitForNextUpdate(int sinceUpdate) throws InterruptedException;
|
||||
|
||||
@@ -149,4 +148,10 @@ public interface TrackedRace {
|
||||
NavigableSet<MarkPassing> getMarkPassings(Competitor competitor);
|
||||
|
||||
void removeWind(Wind wind, WindSource windSource);
|
||||
|
||||
/**
|
||||
* Time stamp that the last event received from the underlying push service carried on it.
|
||||
* Note that these times may not increase monotonically.
|
||||
*/
|
||||
TimePoint getTimePointOfLastEvent();
|
||||
}
|
||||
+3
@@ -119,6 +119,9 @@ public class DynamicTrackedRaceImpl extends TrackedRaceImpl implements
|
||||
competitorMarkPassings.add(markPassing);
|
||||
getMarkPassingsInOrder(markPassing.getWaypoint()).add(markPassing);
|
||||
updated(markPassing.getTimePoint());
|
||||
}
|
||||
// notify *after* all mark passings have been re-established; should avoid flicker
|
||||
for (MarkPassing markPassing : markPassings) {
|
||||
notifyListeners(markPassing);
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -47,6 +47,24 @@ public class TrackedEventImpl implements TrackedEvent {
|
||||
trackedRaces.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removedTrackedRace(TrackedRace trackedRace) {
|
||||
synchronized (trackedRaces) {
|
||||
trackedRaces.remove(trackedRace.getRace());
|
||||
Collection<TrackedRace> trbbc = trackedRacesByBoatClass.get(trackedRace.getRace().getBoatClass());
|
||||
if (trbbc != null) {
|
||||
trbbc.remove(trackedRace);
|
||||
if (trbbc.isEmpty()) {
|
||||
trbbc.remove(trackedRace.getRace().getBoatClass());
|
||||
}
|
||||
}
|
||||
for (RaceListener listener : raceListeners) {
|
||||
listener.raceRemoved(trackedRace);
|
||||
}
|
||||
trackedRaces.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event getEvent() {
|
||||
@@ -79,6 +97,11 @@ public class TrackedEventImpl implements TrackedEvent {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackedRace getExistingTrackedRace(RaceDefinition race) {
|
||||
return trackedRaces.get(race);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRaceListener(RaceListener listener) {
|
||||
|
||||
+11
-8
@@ -77,7 +77,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
if (legStart == null) {
|
||||
return Distance.NULL;
|
||||
} else {
|
||||
MarkPassing legEnd = getTrackedRace().getMarkPassing(getCompetitor(), getLeg().getTo());
|
||||
MarkPassing legEnd = getMarkPassingForLegEnd();
|
||||
TimePoint end = timePoint;
|
||||
if (legEnd != null && timePoint.compareTo(legEnd.getTimePoint()) > 0) {
|
||||
// timePoint is after leg finish; take leg end and end time point
|
||||
@@ -245,7 +245,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
public Speed getAverageVelocityMadeGood(TimePoint timePoint) throws NoWindException {
|
||||
Speed result = null;
|
||||
MarkPassing start = getMarkPassingForLegStart();
|
||||
if (start != null && start.getTimePoint().compareTo(timePoint) > 0) {
|
||||
if (start != null && start.getTimePoint().compareTo(timePoint) <= 0) {
|
||||
MarkPassing end = getMarkPassingForLegEnd();
|
||||
if (end != null) {
|
||||
TimePoint to;
|
||||
@@ -254,11 +254,13 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
} else {
|
||||
to = timePoint;
|
||||
}
|
||||
Position endPos = getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(to, false);
|
||||
Distance d = getWindwardDistance(
|
||||
getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(start.getTimePoint(), false), endPos,
|
||||
to);
|
||||
result = d.inTime(to.asMillis() - start.getTimePoint().asMillis());
|
||||
Position endPos = getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(to, /* extrapolate */ false);
|
||||
if (endPos != null) {
|
||||
Distance d = getWindwardDistance(
|
||||
getTrackedRace().getTrack(getCompetitor())
|
||||
.getEstimatedPosition(start.getTimePoint(), false), endPos, to);
|
||||
result = d.inTime(to.asMillis() - start.getTimePoint().asMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -359,7 +361,8 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
}
|
||||
}
|
||||
|
||||
private SpeedWithBearing getSpeedOverGround(TimePoint at) {
|
||||
@Override
|
||||
public SpeedWithBearing getSpeedOverGround(TimePoint at) {
|
||||
if (hasStartedLeg(at)) {
|
||||
return getTrackedRace().getTrack(getCompetitor()).getEstimatedSpeed(at);
|
||||
} else {
|
||||
|
||||
+7
@@ -46,6 +46,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
private TimePoint start;
|
||||
private TimePoint finish;
|
||||
private TimePoint timePointOfNewestEvent;
|
||||
private TimePoint timePointOfLastEvent;
|
||||
private int updateCount;
|
||||
|
||||
private final Map<TimePoint, TreeSet<Competitor>> competitorRankings;
|
||||
@@ -352,6 +353,11 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
public TimePoint getTimePointOfNewestEvent() {
|
||||
return timePointOfNewestEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getTimePointOfLastEvent() {
|
||||
return timePointOfLastEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeOfEvent may be <code>null</code> meaning to only unblock waiters but not update any time points
|
||||
@@ -366,6 +372,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
if (startOfTracking == null || startOfTracking.compareTo(timeOfEvent) > 0) {
|
||||
startOfTracking = timeOfEvent;
|
||||
}
|
||||
timePointOfLastEvent = timeOfEvent;
|
||||
}
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
+6
@@ -243,4 +243,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getTimePointOfLastEvent() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -160,7 +160,8 @@ public class UDPExpeditionReceiverTest {
|
||||
assertEquals(19, cal.get(Calendar.DAY_OF_MONTH));
|
||||
assertEquals(13, cal.get(Calendar.HOUR_OF_DAY));
|
||||
assertEquals(52, cal.get(Calendar.MINUTE));
|
||||
assertEquals(46, cal.get(Calendar.SECOND));
|
||||
// for some bizarre reason the conversion doesn't seem to be predictable; a calendar / platform issue?
|
||||
assertTrue(cal.get(Calendar.SECOND) == 46 || cal.get(Calendar.SECOND) == 45);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ public class TestStoringAndRetrievingWindTracks extends AbstractTracTracLiveTest
|
||||
public void testStoreAFewWindEntries() throws UnknownHostException, MongoException, InterruptedException {
|
||||
DomainFactory domainFactory = DomainFactory.INSTANCE;
|
||||
Event domainEvent = domainFactory.createEvent(getEvent());
|
||||
DynamicTrackedEvent trackedEvent = domainFactory.trackEvent(domainEvent);
|
||||
DynamicTrackedEvent trackedEvent = domainFactory.getOrCreateTrackedEvent(domainEvent);
|
||||
Iterable<Receiver> typeControllers = domainFactory.getUpdateReceivers(trackedEvent, getEvent(),
|
||||
EmptyWindStore.INSTANCE, ReceiverType.RACECOURSE);
|
||||
addListenersForStoredDataAndStartController(typeControllers);
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ public class MongoWindStoreFactoryImpl implements MongoWindStoreFactory, BundleA
|
||||
}
|
||||
defaultHostName = context.getProperty(MONGO_HOSTNAME);
|
||||
if (defaultHostName == null) {
|
||||
defaultHostName = "localhost";
|
||||
defaultHostName = "127.0.0.1";
|
||||
} else {
|
||||
logger.log(Level.INFO, "found mongo.hostname="+defaultHostName);
|
||||
}
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
/bin
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>com.sap.sailing.server.test</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,8 @@
|
||||
#Mon Jun 13 17:15:33 CEST 2011
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
||||
@@ -0,0 +1,4 @@
|
||||
#Mon Jun 13 17:15:33 CEST 2011
|
||||
eclipse.preferences.version=1
|
||||
pluginProject.extensions=false
|
||||
resolve.requirebundle=false
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Test
|
||||
Bundle-SymbolicName: com.sap.sailing.server.test
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
|
||||
Require-Bundle: com.sap.sailing.server,
|
||||
com.sap.sailing.domain,
|
||||
com.sap.sailing.domain.tractracadapter,
|
||||
org.junit4
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>root</artifactId>
|
||||
<groupId>com.sap.sailing</groupId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>com.sap.sailing</groupId>
|
||||
<artifactId>com.sap.sailing.server.test</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>eclipse-test-plugin</packaging>
|
||||
</project>
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package com.sap.sailing.server.test;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.tracking.RaceListener;
|
||||
import com.sap.sailing.domain.tracking.TrackedEvent;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tractracadapter.RaceHandle;
|
||||
import com.sap.sailing.server.RacingEventServiceImpl;
|
||||
|
||||
public class RaceTrackerTest {
|
||||
private final URL paramUrl;
|
||||
private final URI liveUri;
|
||||
private final URI storedUri;
|
||||
private RacingEventServiceImpl service;
|
||||
private RaceHandle raceHandle;
|
||||
|
||||
public RaceTrackerTest() throws MalformedURLException, URISyntaxException {
|
||||
// for live simulation:
|
||||
// paramUrl = new URL("http://sapsimulation.tracdev.dk/simulateconf/j80race12.txt");
|
||||
// liveUri = new URI("tcp://sapsimulation.tracdev.dk:4420"); // or with tunneling: tcp://localhost:4420
|
||||
// storedUri = new URI("tcp://sapsimulation.tracdev.dk:4421"); // or with tunneling: tcp://localhost:4421
|
||||
// for stored race, non-real-time simulation:
|
||||
paramUrl = new URL("http://germanmaster.traclive.dk/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=bd8c778e-7c65-11e0-8236-406186cbf87c");
|
||||
|
||||
// tunneled:
|
||||
//liveUri = new URI("tcp://localhost:4412");
|
||||
//storedUri = new URI("tcp://localhost:4413");
|
||||
|
||||
//no tunnel:
|
||||
liveUri = new URI("tcp://germanmaster.traclive.dk:4400");
|
||||
storedUri = new URI("tcp://germanmaster.traclive.dk:4401");
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws MalformedURLException, FileNotFoundException, URISyntaxException, InterruptedException {
|
||||
service = new RacingEventServiceImpl();
|
||||
raceHandle = service.addRace(paramUrl, liveUri, storedUri, EmptyWindStore.INSTANCE);
|
||||
}
|
||||
|
||||
private TrackedRace getTrackedRace(TrackedEvent trackedEvent) throws InterruptedException {
|
||||
final TrackedRace[] trackedRaces = new TrackedRace[1];
|
||||
trackedEvent.addRaceListener(new RaceListener() {
|
||||
@Override
|
||||
public void raceAdded(TrackedRace trackedRace) {
|
||||
synchronized (trackedRaces) {
|
||||
trackedRaces[0] = trackedRace;
|
||||
trackedRaces.notifyAll();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void raceRemoved(TrackedRace trackedRace) {
|
||||
}
|
||||
});
|
||||
synchronized (trackedRaces) {
|
||||
if (trackedRaces[0] == null) {
|
||||
trackedRaces.wait();
|
||||
}
|
||||
}
|
||||
return trackedRaces[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitialization() throws InterruptedException {
|
||||
RaceDefinition race = raceHandle.getRace();
|
||||
assertNotNull(race);
|
||||
assertNotNull(getTrackedRace(raceHandle.getTrackedEvent()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopTracking() throws MalformedURLException, IOException, InterruptedException, URISyntaxException {
|
||||
TrackedEvent oldTrackedEvent = raceHandle.getTrackedEvent();
|
||||
TrackedRace oldTrackedRace = getTrackedRace(oldTrackedEvent);
|
||||
service.stopTracking(raceHandle.getEvent());
|
||||
RaceHandle myRaceHandle = service.addRace(paramUrl, liveUri, storedUri, EmptyWindStore.INSTANCE);
|
||||
TrackedEvent newTrackedEvent = myRaceHandle.getTrackedEvent();
|
||||
TrackedRace newTrackedRace = getTrackedRace(newTrackedEvent);
|
||||
// expecting a new tracked race to be created when starting over with tracking
|
||||
assertNotSame(oldTrackedRace, newTrackedRace);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test asserts that tracking the same race twice doesn't create another tracker and in particular no
|
||||
* new tracked event / tracked race.
|
||||
*/
|
||||
@Test
|
||||
public void testTrackingSameRaceWithoutStopping() throws MalformedURLException, IOException, InterruptedException, URISyntaxException {
|
||||
TrackedEvent oldTrackedEvent = raceHandle.getTrackedEvent();
|
||||
TrackedRace oldTrackedRace = getTrackedRace(oldTrackedEvent);
|
||||
RaceHandle myRaceHandle = service.addRace(paramUrl, liveUri, storedUri, EmptyWindStore.INSTANCE);
|
||||
TrackedEvent newTrackedEvent = myRaceHandle.getTrackedEvent();
|
||||
TrackedRace newTrackedRace = getTrackedRace(newTrackedEvent);
|
||||
// expecting a new tracked race to be created when starting over with tracking
|
||||
assertSame(oldTrackedRace, newTrackedRace);
|
||||
assertSame(raceHandle.getRaceTracker(), myRaceHandle.getRaceTracker());
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dorg.osgi.service.http.port=8888"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dorg.osgi.service.http.port=8886"/>
|
||||
<stringAttribute key="pde.version" value="3.3"/>
|
||||
<booleanAttribute key="show_selected_only" value="false"/>
|
||||
<stringAttribute key="target_bundles" value="javax.servlet.jsp@default:default,javax.servlet@default:default,org.apache.ant@default:default,org.apache.commons.el@default:default,org.apache.commons.logging@default:default,org.apache.jasper@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.runtime.compatibility.registry@default:false,org.eclipse.equinox.common@2:true,org.eclipse.equinox.http.jetty@default:default,org.eclipse.equinox.http.registry@default:default,org.eclipse.equinox.http.servlet@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.equinox.util@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi.util@default:default,org.eclipse.osgi@-1:true,org.hamcrest.core@default:default,org.mortbay.jetty.server@default:default,org.mortbay.jetty.util@default:default"/>
|
||||
|
||||
@@ -160,7 +160,7 @@ public class AdminApp extends Servlet {
|
||||
if (race == null) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
TrackedRace trackedRace = getService().getDomainFactory().trackEvent(event).getTrackedRace(race);
|
||||
TrackedRace trackedRace = getService().getDomainFactory().getTrackedEvent(event).getTrackedRace(race);
|
||||
TimePoint time = getTimePoint(req, PARAM_NAME_TIME, PARAM_NAME_TIME_MILLIS, MillisecondsTimePoint.now());
|
||||
TimePoint oneHourLater = new MillisecondsTimePoint(time.asMillis()+3600*1000);
|
||||
String[] latitudes = req.getParameterValues(PARAM_NAME_LATDEG);
|
||||
@@ -202,7 +202,7 @@ public class AdminApp extends Servlet {
|
||||
if (race == null) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
TrackedRace trackedRace = getService().getDomainFactory().trackEvent(event).getTrackedRace(race);
|
||||
TrackedRace trackedRace = getService().getDomainFactory().getTrackedEvent(event).getTrackedRace(race);
|
||||
TimePoint from = getTimePoint(req, PARAM_NAME_FROM_TIME, PARAM_NAME_FROM_TIME_MILLIS,
|
||||
trackedRace.getStart()==null?new MillisecondsTimePoint(0):
|
||||
/* 24h before race start */ new MillisecondsTimePoint(trackedRace.getStart().asMillis()-24*3600*1000));
|
||||
@@ -257,7 +257,7 @@ public class AdminApp extends Servlet {
|
||||
if (race == null) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
TrackedRace trackedRace = getService().getDomainFactory().trackEvent(event)
|
||||
TrackedRace trackedRace = getService().getDomainFactory().getTrackedEvent(event)
|
||||
.getTrackedRace(race);
|
||||
trackedRace.setWindSource(windSource);
|
||||
resp.getWriter().println(
|
||||
@@ -313,7 +313,7 @@ public class AdminApp extends Servlet {
|
||||
try {
|
||||
TimePoint timePoint = getTimePoint(req, PARAM_NAME_TIME, PARAM_NAME_TIME_MILLIS, MillisecondsTimePoint.now());
|
||||
Wind wind = new WindImpl(p, timePoint, speed);
|
||||
getService().getDomainFactory().trackEvent(event).getTrackedRace(race).recordWind(wind, WindSource.WEB);
|
||||
getService().getDomainFactory().getTrackedEvent(event).getTrackedRace(race).recordWind(wind, WindSource.WEB);
|
||||
} catch (InvalidDateException e) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Couldn't parse time specification " + e.getMessage());
|
||||
}
|
||||
@@ -342,7 +342,7 @@ public class AdminApp extends Servlet {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Wind source name " + sourceName + " unknown");
|
||||
} else {
|
||||
try {
|
||||
WindTrack windTrack = getService().getDomainFactory().trackEvent(event)
|
||||
WindTrack windTrack = getService().getDomainFactory().getTrackedEvent(event)
|
||||
.getTrackedRace(race).getWindTrack(windSource);
|
||||
TimePoint timePoint = getTimePoint(req, PARAM_NAME_TIME, PARAM_NAME_TIME_MILLIS,
|
||||
MillisecondsTimePoint.now());
|
||||
|
||||
@@ -169,7 +169,7 @@ public class ModeratorApp extends Servlet {
|
||||
RaceDefinition race = getRaceDefinition(req);
|
||||
TrackedRace trackedRace = null;
|
||||
if (event != null && race != null) {
|
||||
trackedRace = getService().getDomainFactory().trackEvent(event).getTrackedRace(race);
|
||||
trackedRace = getService().getDomainFactory().getTrackedEvent(event).getTrackedRace(race);
|
||||
}
|
||||
return trackedRace;
|
||||
}
|
||||
@@ -183,7 +183,7 @@ public class ModeratorApp extends Servlet {
|
||||
} else {
|
||||
try {
|
||||
TimePoint timePoint = getTimePoint(req, PARAM_NAME_TIME, PARAM_NAME_TIME_MILLIS,
|
||||
trackedRace.getTimePointOfNewestEvent()==null?MillisecondsTimePoint.now():trackedRace.getTimePointOfNewestEvent());
|
||||
trackedRace.getTimePointOfLastEvent()==null?MillisecondsTimePoint.now():trackedRace.getTimePointOfLastEvent());
|
||||
String sinceUpdateString = req.getParameter(PARAM_NAME_SINCE_UPDATE);
|
||||
if (sinceUpdateString != null) {
|
||||
System.out.println("Blocking...");
|
||||
@@ -200,6 +200,8 @@ public class ModeratorApp extends Servlet {
|
||||
.asMillis());
|
||||
jsonRace.put("timeofnewestevent", trackedRace.getTimePointOfNewestEvent() == null ? 0l : trackedRace
|
||||
.getTimePointOfNewestEvent().asMillis());
|
||||
jsonRace.put("timeoflastevent", trackedRace.getTimePointOfLastEvent() == null ? 0l : trackedRace
|
||||
.getTimePointOfLastEvent().asMillis());
|
||||
jsonRace.put("updatecount", trackedRace.getUpdateCount());
|
||||
Position positionForWind = null;
|
||||
TrackedLeg currentLeg = trackedRace.getCurrentLeg(timePoint);
|
||||
@@ -234,6 +236,11 @@ public class ModeratorApp extends Servlet {
|
||||
TrackedLegOfCompetitor trackedLegOfCompetitor = leg.getTrackedLeg(competitor);
|
||||
if (trackedLegOfCompetitor != null) {
|
||||
jsonCompetitorInLeg.put("name", competitor.getName());
|
||||
Speed currentSpeedOverGround = trackedLegOfCompetitor.getSpeedOverGround(timePoint);
|
||||
if (currentSpeedOverGround != null) {
|
||||
jsonCompetitorInLeg.put("currentSpeedOverGroundInKnots",
|
||||
currentSpeedOverGround == null ? null : currentSpeedOverGround.getKnots());
|
||||
}
|
||||
Speed averageSpeedOverGround = trackedLegOfCompetitor.getAverageSpeedOverGround(timePoint);
|
||||
if (averageSpeedOverGround != null) {
|
||||
jsonCompetitorInLeg.put("averageSpeedOverGroundInKnots",
|
||||
@@ -347,7 +354,7 @@ public class ModeratorApp extends Servlet {
|
||||
JSONObject jsonRace = new JSONObject();
|
||||
jsonRace.put("name", race.getName());
|
||||
jsonRace.put("boatclass", race.getBoatClass()==null?"":race.getBoatClass().getName());
|
||||
TimePoint start = getService().getDomainFactory().trackEvent(event).getTrackedRace(race).getStart();
|
||||
TimePoint start = getService().getDomainFactory().getTrackedEvent(event).getTrackedRace(race).getStart();
|
||||
jsonRace.put("start", start==null?Long.MAX_VALUE:start.asMillis());
|
||||
JSONArray jsonLegs = new JSONArray();
|
||||
for (Leg leg : race.getCourse().getLegs()) {
|
||||
@@ -356,7 +363,7 @@ public class ModeratorApp extends Servlet {
|
||||
jsonLeg.put("end", leg.getTo().getName());
|
||||
jsonLegs.add(jsonLeg);
|
||||
}
|
||||
TrackedRace trackedRace = getService().getDomainFactory().trackEvent(event).getTrackedRace(race);
|
||||
TrackedRace trackedRace = getService().getDomainFactory().getOrCreateTrackedEvent(event).getTrackedRace(race);
|
||||
if (trackedRace.getStart() != null) {
|
||||
jsonRace.put("start", trackedRace.getStart().asMillis());
|
||||
}
|
||||
|
||||
@@ -12,11 +12,32 @@ import java.text.ParseException;
|
||||
import com.sap.sailing.declination.DeclinationService;
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.tracking.TrackedEvent;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tractracadapter.DomainFactory;
|
||||
import com.sap.sailing.domain.tractracadapter.RaceHandle;
|
||||
import com.sap.sailing.domain.tractracadapter.RaceTracker;
|
||||
import com.sap.sailing.util.Util.Triple;
|
||||
|
||||
/**
|
||||
* An OSGi service that can be used to track boat races using a TracTrac connector that pushes
|
||||
* live GPS boat location, waypoint, coarse and mark passing data.<p>
|
||||
*
|
||||
* If a race/event is already being tracked, another {@link #addRace(URL, URI, URI, WindStore)} or
|
||||
* {@link #addEvent(URL, URI, URI, WindStore)} call will have no effect, even if a different
|
||||
* {@link WindStore} is requested.<p>
|
||||
*
|
||||
* TODO When the tracking of a race/event is {@link #stopTracking(Event, RaceDefinition) stopped}, the next
|
||||
* time it's started to be tracked, a new {@link TrackedRace} at least will be constructed. This also
|
||||
* means that when a {@link TrackedEvent} exists that still holds other {@link TrackedRace}s, the
|
||||
* no longer tracked {@link TrackedRace} will be removed from the {@link TrackedEvent}.
|
||||
* corresponding information is removed also from the {@link DomainFactory}'s caches to ensure that
|
||||
* clean, fresh data is received should another tracking request be issued later.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface RacingEventService {
|
||||
Iterable<Event> getAllEvents();
|
||||
|
||||
@@ -25,38 +46,46 @@ public interface RacingEventService {
|
||||
DomainFactory getDomainFactory();
|
||||
|
||||
/**
|
||||
* Defines the event and for each race listed in the JSON document creates a {@link RaceTracker} that
|
||||
* tracks the respective race. The {@link RaceDefinition}s obtained this way are all grouped into
|
||||
* the single {@link Event} produced for the event listed in the JSON response. Note that the
|
||||
* many race trackers will have their TracTrac <code>Event</code> each, all with the same name,
|
||||
* meaning the same event but being distinct.
|
||||
* Defines the event and for each race listed in the JSON document that is not already being tracked by this service
|
||||
* creates a {@link RaceTracker} that starts tracking the respective race. The {@link RaceDefinition}s obtained this
|
||||
* way are all grouped into the single {@link Event} produced for the event listed in the JSON response. Note that
|
||||
* the many race trackers will have their TracTrac <code>Event</code> each, all with the same name, meaning the same
|
||||
* event but being distinct.
|
||||
*
|
||||
* @param jsonURL
|
||||
* URL of a JSON response that contains an "event" object telling the event's name and ID, as well as a
|
||||
* JSON array named "races" which tells ID and replay URL for the race. From those replay URLs the
|
||||
* paramURL for the Java client can be derived.
|
||||
* @param windStore TODO
|
||||
*/
|
||||
void addEvent(URL jsonURL, URI liveURI, URI storedURI, WindStore windStore) throws MalformedURLException, FileNotFoundException,
|
||||
Event addEvent(URL jsonURL, URI liveURI, URI storedURI, WindStore windStore) throws MalformedURLException, FileNotFoundException,
|
||||
URISyntaxException, IOException, ParseException, org.json.simple.parser.ParseException;
|
||||
|
||||
/**
|
||||
* Adds a single race tracker, using the race's parameter URL which delivers the single configuration
|
||||
* text file for that race. While the result of passing this URL to the TracTrac <code>KeyValue.setup</code>
|
||||
* is a TracTrac <code>Event</code>, those events only manage a single race. In our domain model, we group
|
||||
* those races into a single instance of our {@link Event} class.<p>
|
||||
* If not already tracking the URL/URI/URI combination, adds a single race tracker and starts tracking the race,
|
||||
* using the race's parameter URL which delivers the single configuration text file for that race. While the result
|
||||
* of passing this URL to the TracTrac <code>KeyValue.setup</code> is a TracTrac <code>Event</code>, those events
|
||||
* only manage a single race. In our domain model, we group those races into a single instance of our {@link Event}
|
||||
* class.
|
||||
* <p>
|
||||
*
|
||||
* If this is the first race of an event, the {@link Event} is created as well. If the {@link RaceDefinition} for
|
||||
* the race already exists, it isn't created again. Also, if a {@link RaceTracker} for the given race already
|
||||
* exists, it is not added again.
|
||||
* @param windStore TODO
|
||||
* exists, it is not added again.<p>
|
||||
*
|
||||
* Note that when the race identified by <code>paramURL</code>, <code>liveURI</code> and <code>storedURI</code> is
|
||||
* already being tracked, then regardless of the <code>windStore</code> selection the existing tracker will be used
|
||||
* and its race handle will be returned. A log message will indicate a potential wind store mismatch (based on
|
||||
* {@link WindStore#equals(Object)}).
|
||||
*/
|
||||
void addRace(URL paramURL, URI liveURI, URI storedURI, WindStore windStore) throws MalformedURLException, FileNotFoundException,
|
||||
RaceHandle addRace(URL paramURL, URI liveURI, URI storedURI, WindStore windStore) throws MalformedURLException, FileNotFoundException,
|
||||
URISyntaxException;
|
||||
|
||||
/**
|
||||
* Stops tracking all races of the event specified. This will also stop tracking wind for all races of this event.
|
||||
* See {@link #stopTrackingWind(Event, RaceDefinition)}.
|
||||
* See {@link #stopTrackingWind(Event, RaceDefinition)}. If there were multiple calls to
|
||||
* {@link #addRace(URL, URI, URI, WindStore)} with an equal combination of URLs/URIs, the {@link RaceTracker}
|
||||
* already tracking the race was re-used. The trackers will be stopped by this call regardless of how many calls
|
||||
* were made that ensured they were tracking.
|
||||
*/
|
||||
void stopTracking(Event event) throws MalformedURLException, IOException, InterruptedException;
|
||||
|
||||
|
||||
+53
-27
@@ -16,6 +16,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.declination.DeclinationService;
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
@@ -25,6 +26,7 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tractracadapter.DomainFactory;
|
||||
import com.sap.sailing.domain.tractracadapter.JSONService;
|
||||
import com.sap.sailing.domain.tractracadapter.RaceHandle;
|
||||
import com.sap.sailing.domain.tractracadapter.RaceRecord;
|
||||
import com.sap.sailing.domain.tractracadapter.RaceTracker;
|
||||
import com.sap.sailing.expeditionconnector.UDPExpeditionReceiver;
|
||||
@@ -33,11 +35,13 @@ import com.sap.sailing.util.Util.Pair;
|
||||
import com.sap.sailing.util.Util.Triple;
|
||||
|
||||
public class RacingEventServiceImpl implements RacingEventService {
|
||||
private static final Logger logger = Logger.getLogger(RacingEventServiceImpl.class.getName());
|
||||
|
||||
private final DomainFactory domainFactory;
|
||||
|
||||
private final Map<String, Event> eventsByName;
|
||||
|
||||
private final Map<Event, Set<RaceTracker>> raceTrackers;
|
||||
private final Map<Event, Set<RaceTracker>> raceTrackersByEvent;
|
||||
|
||||
/**
|
||||
* Remembers the wind tracker and the port on which the UDP receiver with which the wind tracker is
|
||||
@@ -45,13 +49,19 @@ public class RacingEventServiceImpl implements RacingEventService {
|
||||
*/
|
||||
private final Map<RaceDefinition, Pair<WindTracker, Integer>> windTrackers;
|
||||
|
||||
/**
|
||||
* Remembers the trackers by paramURL/liveURI/storedURI to avoid duplication
|
||||
*/
|
||||
private final Map<Triple<URL, URI, URI>, RaceTracker> raceTrackersByURLs;
|
||||
|
||||
private final Map<Integer, UDPExpeditionReceiver> windReceivers;
|
||||
|
||||
public RacingEventServiceImpl() {
|
||||
domainFactory = DomainFactory.INSTANCE;
|
||||
eventsByName = new HashMap<String, Event>();
|
||||
raceTrackers = new HashMap<Event, Set<RaceTracker>>();
|
||||
raceTrackersByEvent = new HashMap<Event, Set<RaceTracker>>();
|
||||
windTrackers = new HashMap<RaceDefinition, Pair<WindTracker, Integer>>();
|
||||
raceTrackersByURLs = new HashMap<Triple<URL, URI, URI>, RaceTracker>();
|
||||
windReceivers = new HashMap<Integer, UDPExpeditionReceiver>();
|
||||
}
|
||||
|
||||
@@ -71,42 +81,57 @@ public class RacingEventServiceImpl implements RacingEventService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEvent(URL jsonURL, URI liveURI, URI storedURI, WindStore windStore) throws URISyntaxException, IOException, ParseException, org.json.simple.parser.ParseException {
|
||||
public Event addEvent(URL jsonURL, URI liveURI, URI storedURI, WindStore windStore) throws URISyntaxException, IOException, ParseException, org.json.simple.parser.ParseException {
|
||||
JSONService jsonService = getDomainFactory().parseJSONURL(jsonURL);
|
||||
Event event = null;
|
||||
for (RaceRecord rr : jsonService.getRaceRecords()) {
|
||||
URL paramURL = rr.getParamURL();
|
||||
addRace(paramURL, liveURI, storedURI, windStore);
|
||||
event = addRace(paramURL, liveURI, storedURI, windStore).getEvent();
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRace(URL paramURL, URI liveURI, URI storedURI, WindStore windStore) throws MalformedURLException, FileNotFoundException,
|
||||
public RaceHandle addRace(URL paramURL, URI liveURI, URI storedURI, WindStore windStore) throws MalformedURLException, FileNotFoundException,
|
||||
URISyntaxException {
|
||||
RaceTracker tracker = getDomainFactory().createRaceTracker(paramURL, liveURI, storedURI, windStore);
|
||||
Set<RaceTracker> trackers = raceTrackers.get(tracker.getEvent());
|
||||
if (trackers == null) {
|
||||
trackers = new HashSet<RaceTracker>();
|
||||
raceTrackers.put(tracker.getEvent(), trackers);
|
||||
}
|
||||
trackers.add(tracker);
|
||||
String eventName = tracker.getEvent().getName();
|
||||
Event eventWithName = eventsByName.get(eventName);
|
||||
if (eventWithName != null) {
|
||||
if (eventWithName != tracker.getEvent()) {
|
||||
throw new RuntimeException("Internal error. Two Event objects with equal name "+eventName);
|
||||
Triple<URL, URI, URI> key = new Triple<URL, URI, URI>(paramURL, liveURI, storedURI);
|
||||
RaceTracker tracker = raceTrackersByURLs.get(key);
|
||||
if (tracker == null) {
|
||||
tracker = getDomainFactory().createRaceTracker(paramURL, liveURI, storedURI, windStore);
|
||||
raceTrackersByURLs.put(key, tracker);
|
||||
Set<RaceTracker> trackers = raceTrackersByEvent.get(tracker.getEvent());
|
||||
if (trackers == null) {
|
||||
trackers = new HashSet<RaceTracker>();
|
||||
raceTrackersByEvent.put(tracker.getEvent(), trackers);
|
||||
}
|
||||
trackers.add(tracker);
|
||||
String eventName = tracker.getEvent().getName();
|
||||
Event eventWithName = eventsByName.get(eventName);
|
||||
if (eventWithName != null) {
|
||||
if (eventWithName != tracker.getEvent()) {
|
||||
throw new RuntimeException("Internal error. Two Event objects with equal name "+eventName);
|
||||
}
|
||||
} else {
|
||||
eventsByName.put(eventName, tracker.getEvent());
|
||||
}
|
||||
} else {
|
||||
eventsByName.put(eventName, tracker.getEvent());
|
||||
WindStore existingTrackersWindStore = tracker.getWindStore();
|
||||
if (!existingTrackersWindStore.equals(windStore)) {
|
||||
logger.warning("Wind store mismatch. Requested wind store: "+windStore+
|
||||
". Wind store in use by existing tracker: "+existingTrackersWindStore);
|
||||
}
|
||||
}
|
||||
return tracker.getRaceHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopTracking(Event event) throws MalformedURLException, IOException, InterruptedException {
|
||||
if (raceTrackers.containsKey(event)) {
|
||||
for (RaceTracker raceTracker : raceTrackers.get(event)) {
|
||||
raceTracker.stop();
|
||||
if (raceTrackersByEvent.containsKey(event)) {
|
||||
for (RaceTracker raceTracker : raceTrackersByEvent.get(event)) {
|
||||
raceTracker.stop(); // this also removes the TrackedRace from trackedEvent
|
||||
raceTrackersByURLs.remove(raceTracker.getURLs());
|
||||
}
|
||||
raceTrackers.remove(event);
|
||||
raceTrackersByEvent.remove(event);
|
||||
}
|
||||
if (event != null && event.getName() != null) {
|
||||
eventsByName.remove(event.getName());
|
||||
@@ -118,18 +143,19 @@ public class RacingEventServiceImpl implements RacingEventService {
|
||||
|
||||
@Override
|
||||
public void stopTracking(Event event, RaceDefinition race) throws MalformedURLException, IOException, InterruptedException {
|
||||
if (raceTrackers.containsKey(event)) {
|
||||
Iterator<RaceTracker> trackerIter = raceTrackers.get(event).iterator();
|
||||
if (raceTrackersByEvent.containsKey(event)) {
|
||||
Iterator<RaceTracker> trackerIter = raceTrackersByEvent.get(event).iterator();
|
||||
while (trackerIter.hasNext()) {
|
||||
RaceTracker raceTracker = trackerIter.next();
|
||||
if (raceTracker.getRace() == race) {
|
||||
raceTracker.stop();
|
||||
raceTracker.stop(); // this also removes the TrackedRace from trackedEvent
|
||||
trackerIter.remove();
|
||||
raceTrackersByURLs.remove(raceTracker.getURLs());
|
||||
}
|
||||
}
|
||||
}
|
||||
// if the last tracked race was removed, remove the entire event
|
||||
if (raceTrackers.get(event).isEmpty()) {
|
||||
if (raceTrackersByEvent.get(event).isEmpty()) {
|
||||
stopTracking(event);
|
||||
}
|
||||
}
|
||||
@@ -138,7 +164,7 @@ public class RacingEventServiceImpl implements RacingEventService {
|
||||
public synchronized void startTrackingWind(Event event, RaceDefinition race, int port,
|
||||
DeclinationService declinationService) throws SocketException {
|
||||
if (!windTrackers.containsKey(race)) {
|
||||
DynamicTrackedEvent trackedEvent = getDomainFactory().trackEvent(event);
|
||||
DynamicTrackedEvent trackedEvent = getDomainFactory().getOrCreateTrackedEvent(event);
|
||||
DynamicTrackedRace trackedRace = trackedEvent.getTrackedRace(race);
|
||||
WindTracker windTracker = new WindTracker(trackedRace, declinationService);
|
||||
UDPExpeditionReceiver receiver = getOrCreateWindReceiverForPort(port);
|
||||
|
||||
Reference in new issue
Block a user