add race-listener; prepare race-change-listener as leg-change-listener; cleanup cache and race-change-listener on remove race;

This commit is contained in:
Christopher Ronnewinkel (D036654)
2015-03-19 16:02:55 +01:00
parent 044c0d9bda
commit 19fec74ab0
4 changed files with 191 additions and 50 deletions
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.common;
public interface LegIdentifier extends RegattaAndRaceIdentifier {
RaceIdentifier getRaceIdentifier();
String getLegName();
int getLegNumber();
}
@@ -2,15 +2,23 @@ package com.sap.sailing.domain.common;
public class LegIdentifierImpl extends RegattaNameAndRaceName implements LegIdentifier {
private static final long serialVersionUID = 3599904513673776450L;
private RaceIdentifier raceIdentifier = null;
private String legName;
LegIdentifierImpl() {}
public LegIdentifierImpl(RegattaAndRaceIdentifier raceIdentifier, String legName) {
super(raceIdentifier.getRegattaName(), raceIdentifier.getRaceName());
this.raceIdentifier = raceIdentifier;
this.legName = legName;
}
@Override
public RaceIdentifier getRaceIdentifier() {
return this.raceIdentifier;
}
@Override
public String getLegName() {
return legName;
@@ -66,7 +66,7 @@ public class RaceSimulationOverlay extends FullCanvasOverlay {
}
public void updateLeg(int newLeg, boolean clearCanvas, int newVersion) {
if (((newLeg != raceLeg)||((newLeg == raceLeg)&&(newVersion != 0)&&(newVersion != (simulationResult==null ? 0 : simulationResult.getVersion())))) && (this.isVisible())) {
if (((newLeg != raceLeg)||((newLeg == raceLeg)&&(newVersion != (simulationResult==null ? 0 : simulationResult.getVersion())))) && (this.isVisible())) {
raceLeg = newLeg;
if (clearCanvas) {
this.clearCanvas();
@@ -18,14 +18,26 @@ import java.util.logging.Logger;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.LegIdentifier;
import com.sap.sailing.domain.common.LegIdentifierImpl;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.RaceListener;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.server.RacingEventService;
@@ -46,6 +58,7 @@ import com.sap.sailing.simulator.windfield.impl.WindFieldTrackedRaceImpl;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.util.SmartFutureCache;
public class SimulationServiceImpl implements SimulationService {
@@ -56,7 +69,8 @@ public class SimulationServiceImpl implements SimulationService {
final private SmartFutureCache<LegIdentifier, SimulationResults, SmartFutureCache.EmptyUpdateInterval> cache;
final private RacingEventService racingEventService;
final private ScheduledExecutorService scheduler;
final private HashMap<LegIdentifier, Listener> listeners;
final private HashMap<String, SimulationRaceListener> raceListeners;
final private HashMap<RaceIdentifier, LegChangeListener> legListeners;
final private long WAIT_MILLIS = 20000; // milliseconds to wait until earliest cache-update for simulation
public SimulationServiceImpl(Executor executor, RacingEventService racingEventService) {
@@ -64,7 +78,8 @@ public class SimulationServiceImpl implements SimulationService {
this.scheduler = Executors.newScheduledThreadPool(1);
this.racingEventService = racingEventService;
if (racingEventService != null) {
this.listeners = new HashMap<LegIdentifier, Listener>();
this.raceListeners = new HashMap<String, SimulationRaceListener>();
this.legListeners = new HashMap<RaceIdentifier, LegChangeListener>();
this.cache = new SmartFutureCache<LegIdentifier, SimulationResults, SmartFutureCache.EmptyUpdateInterval>(
new SmartFutureCache.AbstractCacheUpdater<LegIdentifier, SimulationResults, SmartFutureCache.EmptyUpdateInterval>() {
@Override
@@ -76,75 +91,177 @@ public class SimulationServiceImpl implements SimulationService {
}
}, "SmartFutureCache.simulationService (" + racingEventService.toString() + ")");
} else {
this.listeners = null;
this.raceListeners = null;
this.legListeners = null;
this.cache = null;
}
}
// TODO: subscribe to race-admin-events; cleanup listeners when tracked-race is removed from server => admin-console?
private class Listener extends AbstractRaceChangeListener {
private final TrackedRace trackedRace;
private final LegIdentifier legIdentifier;
private final ScheduledExecutorService scheduler;
private boolean finished;
private boolean covered;
public Listener(TrackedRace trackedRace, LegIdentifier legIdentifier, ScheduledExecutorService scheduler) {
this.trackedRace = trackedRace;
this.legIdentifier = legIdentifier;
this.scheduler = scheduler;
this.finished = false;
this.covered = false;
private class SimulationRaceListener implements RaceListener {
@Override
public void raceAdded(TrackedRace trackedRace) {
}
@Override
protected void defaultAction() {
if (!this.finished) {
if (!this.covered) {
this.covered = true;
scheduler.schedule(() -> triggerUpdate(legIdentifier), WAIT_MILLIS, TimeUnit.MILLISECONDS);
public void raceRemoved(TrackedRace trackedRace) {
RaceIdentifier raceIdentifier = trackedRace.getRaceIdentifier();
LegChangeListener listener = legListeners.get(raceIdentifier);
if (listener != null) {
trackedRace.removeListener(listener);
}
int legNumber = 1;
Iterator<TrackedLeg> iterator = trackedRace.getTrackedLegs().iterator();
while (iterator.hasNext()) {
LegIdentifier key = new LegIdentifierImpl((RegattaAndRaceIdentifier)raceIdentifier, ""+legNumber);
SimulationResults entry = cache.get(key, false);
if (entry != null) {
cache.remove(key);
}
legNumber++;
iterator.next();
}
legListeners.remove(raceIdentifier);
}
}
private class LegChangeListener extends AbstractRaceChangeListener {
private final TrackedRace trackedRace;
private LegIdentifier legIdentifier;
private final ScheduledExecutorService scheduler;
private boolean covered;
public LegChangeListener(TrackedRace trackedRace, ScheduledExecutorService scheduler) {
this.trackedRace = trackedRace;
this.legIdentifier = null;
this.scheduler = scheduler;
this.covered = false;
}
public boolean isLive() {
return trackedRace.isLive(MillisecondsTimePoint.now());
}
public void setLegIdentifier(LegIdentifier legIdentifier) {
if (this.legIdentifier != null) {
if (!this.legIdentifier.equals(legIdentifier)) {
this.legIdentifier = legIdentifier;
this.covered = false;
}
} else {
this.legIdentifier = legIdentifier;
}
}
@Override
protected void defaultAction() {
if ((!this.covered)&&(legIdentifier!=null)) {
this.covered = true;
LegIdentifier tmpLegIdentifier = new LegIdentifierImpl(legIdentifier, legIdentifier.getLegName());
scheduler.schedule(() -> triggerUpdate(tmpLegIdentifier), WAIT_MILLIS, TimeUnit.MILLISECONDS);
}
}
private void triggerUpdate(LegIdentifier legIdentifier) {
this.covered = false;
if (this.legIdentifier.equals(legIdentifier)) {
this.covered = false;
}
logger.info("Simulation Scheduled Update Triggered: \"" + legIdentifier.toString() + "\"");
cache.triggerUpdate(legIdentifier, null);
}
@Override
public void startOfRaceChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace) {
// relevant for simulation: start of leg 1 changes; requires update of leg 1
// TODO: update leg 1
}
@Override
public void statusChanged(TrackedRaceStatus newStatus) {
// relevant for simulation: update all legs when switched to "replay"
// TODO: update all legs
}
@Override
public void windSourcesToExcludeChanged(Iterable<? extends WindSource> windSourcesToExclude) {
// relevant for simulation: update all legs when wind changes overall
// TODO: update all legs
}
@Override
public void startOfTrackingChanged(TimePoint startOfTracking) {
// irrelevant for simulation
}
@Override
public void endOfTrackingChanged(TimePoint endOfTracking) {
// irrelevant for simulation
}
@Override
public void startTimeReceivedChanged(TimePoint startTimeReceived) {
// irrelevant for simulation: should be covered by startOfRaceChanged()
}
@Override
public void markPositionChanged(GPSFix fix, Mark mark, boolean firstInTrack) {
// relevant for simulation
// TODO: identify influenced legs and update these legs
}
@Override
public void windDataReceived(Wind wind, WindSource windSource) {
// relevant for simulation: update legs influenced by wind
// TODO: update influenced legs; "live" update current leg
defaultAction();
}
@Override
public void windDataRemoved(Wind wind, WindSource windSource) {
// relevant for simulation: update legs influenced by wind
// TODO: update influenced legs; "live" update current leg
defaultAction();
}
@Override
public void windAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage) {
// relevant for simulation: update legs influenced by wind
// TODO: update influenced legs; "live" update current leg
defaultAction();
}
@Override
public void competitorPositionChanged(GPSFixMoving fix, Competitor item) {
// irrelevant for simulation: covered by wind estimation
}
@Override
public void markPassingReceived(Competitor competitor, Map<Waypoint, MarkPassing> oldMarkPassings, Iterable<MarkPassing> markPassings) {
if ((trackedRace != null)&&(!this.finished)) {
Leg leg = trackedRace.getRace().getCourse().getLegs().get(legIdentifier.getLegNumber());
// get next mark as end-position
Waypoint toWaypoint = leg.getTo();
MarkPassing markPassing;
Iterator<MarkPassing> markPassingIterator = trackedRace.getMarkPassingsInOrder(toWaypoint).iterator();
if (markPassingIterator.hasNext()) {
markPassing = markPassingIterator.next();
} else {
markPassing = null;
}
if (markPassing != null) { // if leg is finished, only one last simulation update required
defaultAction();
this.finished = true;
} else {
this.finished = false;
}
}
// relevant for simulation: update start- and end-times of legs
// TODO: update influenced legs; "live" update current leg
}
@Override
public void speedAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage) {
// irrelevant for simulation
}
// simulation is not influenced by live-delay
@Override
public void delayToLiveChanged(long delayToLiveInMillis) {
// irrelevant for simulation
}
@Override
public void competitorPositionChanged(GPSFixMoving fix, Competitor competitor) {
public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) {
// relevant for simulation: update legs influenced waypoint
// TODO: update influenced legs; "live" update current leg
}
@Override
public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) {
// relevant for simulation: update legs influenced waypoint
// TODO: update influenced legs; "live" update current leg
}
}
@@ -162,13 +279,28 @@ public class SimulationServiceImpl implements SimulationService {
SimulationResults result = cache.get(legIdentifier, false);
if (result == null) {
logger.fine("Simulation Get: Cache Empty: \"" + legIdentifier.toString() + "\"");
if (!listeners.containsKey(legIdentifier)) {
if (!raceListeners.containsKey(legIdentifier.getRegattaName())) {
Regatta regatta = racingEventService.getRegattaByName(legIdentifier.getRegattaName());
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(regatta);
SimulationRaceListener raceListener = new SimulationRaceListener();
raceListeners.put(legIdentifier.getRegattaName(), raceListener);
trackedRegatta.addRaceListener(raceListener);
}
if (!legListeners.containsKey(legIdentifier.getRaceIdentifier())) {
TrackedRace trackedRace = racingEventService.getTrackedRace(legIdentifier);
if (trackedRace != null) {
Listener listener = new Listener(trackedRace, legIdentifier, scheduler);
listeners.put(legIdentifier, listener);
LegChangeListener listener = new LegChangeListener(trackedRace, scheduler);
if (listener.isLive()) {
listener.setLegIdentifier(legIdentifier);
}
legListeners.put(legIdentifier.getRaceIdentifier(), listener);
trackedRace.addListener(listener);
}
} else {
LegChangeListener listener = legListeners.get(legIdentifier.getRaceIdentifier());
if (listener.isLive()) {
listener.setLegIdentifier(legIdentifier);
}
}
logger.info("Simulation Get: Update Triggered: \"" + legIdentifier.toString() + "\"");
cache.triggerUpdate(legIdentifier, null);