mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-25 06:58:39 +00:00
Merge branch 'master' into prod
Conflicts: java/pom.xml
This commit is contained in:
@@ -1 +1,3 @@
|
||||
.metadata
|
||||
*.class
|
||||
*.swp
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
Axel
|
||||
====
|
||||
|
||||
- Implement and test course update with corresponding TrackedLeg[OfCompetitor] updates
|
||||
|
||||
- Clarify Equinox server upgrade process from the maven-repository
|
||||
|
||||
- Open test server firewall ports for UDP / Expedition wind integration
|
||||
|
||||
- test UDP ports
|
||||
|
||||
- Set up two Expedition machines with RF and UMTS transmission, test killing one
|
||||
|
||||
- enable switching between our and TracTrac-provided leaderboard information
|
||||
|
||||
- Test fail-over in case one of the two Java VM fails
|
||||
|
||||
- Pass final production instance URL information to BeTomorrow for wind integration
|
||||
|
||||
- Display gain/loss for next-best competitor (green/red arrow up/down) based on VMG comparison
|
||||
|
||||
- Improve Mongo-based tests by using Mongo transactions instead of waiting for prior transactions to complete
|
||||
|
||||
|
||||
Simon
|
||||
=====
|
||||
|
||||
+ Server: Mehr RAM bestellen
|
||||
+ Prüfen, warum in der Map Anzeige manchmal ein Competitor doppelt ist
|
||||
+ Wind aus der Map löschen wenn der sich geändert hat
|
||||
+ Simulator mit Speed = 1 anschmeissen und dann prüfen was passiert
|
||||
+ Prüfen warum im live leaderbaord manchmal 4 legs angezeigt werden
|
||||
+ Prüfen warum der Listener thread manchmal nicht richtig gestoppt wird (disconnect sollte auch thread stoppen)
|
||||
+ Datenbank nicht einfach so löschen (Wind Positionen)
|
||||
+ Infinity oder große Werte als Ankerindikatoren erkennen und anzeigen können
|
||||
+ Anzeige Truebearing bei Wind-Informationen
|
||||
+ MongoDB als Windsource bei addrace oder addevent einstellbar machen
|
||||
+ Anzeigen im Leaderboard ob der Wind up oder down ist (competitor.values wäre ein guter platz dafür)
|
||||
+ Implementierung Leaderboard auf Basis HTML Designer
|
||||
+ Wenn Axel Branches eingerichtet hat, dann MongoDBs auf Server startbar machen über supervisord
|
||||
@@ -1,18 +0,0 @@
|
||||
- Implement and test course update with corresponding TrackedLeg[OfCompetitor] updates
|
||||
|
||||
- Clarify Equinox server upgrade process from the maven-repository
|
||||
|
||||
- Open test server firewall ports for UDP / Expedition wind integration
|
||||
|
||||
- Set up two Expedition machines with RF and UMTS transmission, test killing one
|
||||
|
||||
- enable switching between our and TracTrac-provided leaderboard information
|
||||
|
||||
- Test fail-over in case one of the two Java VM fails
|
||||
|
||||
- Pass final production instance URL information to BeTomorrow for wind integration
|
||||
|
||||
- Display gain/loss for next-best competitor (green/red arrow up/down) based on VMG comparison
|
||||
|
||||
- Improve Mongo-based tests by using Mongo transactions instead of waiting for prior transactions to complete
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ public class DeclinationServiceTest extends AbstractDeclinationTest {
|
||||
@Test
|
||||
public void testSimpleDeclinationQueryMatchedInStore() throws IOException, ClassNotFoundException, ParseException {
|
||||
Declination result = service.getDeclination(new MillisecondsTimePoint(simpleDateFormat.parse("2011-02-03").getTime()),
|
||||
new DegreePosition(51, -5), /* timeoutForOnlineFetchInMilliseconds */ 1000);
|
||||
new DegreePosition(51, -5), /* timeoutForOnlineFetchInMilliseconds */ 3000);
|
||||
assertEquals(-3.-14./60., result.getBearing().getDegrees(), 0.0000001);
|
||||
assertEquals(0.+09./60., result.getAnnualChange().getDegrees(), 0.0000001);
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public class DeclinationServiceTest extends AbstractDeclinationTest {
|
||||
@Test
|
||||
public void testDeclinationQueryNotMatchedInStore() throws IOException, ClassNotFoundException, ParseException {
|
||||
Declination result = service.getDeclination(new MillisecondsTimePoint(simpleDateFormat.parse("2010-02-03").getTime()),
|
||||
new DegreePosition(51, -5), /* timeoutForOnlineFetchInMilliseconds */ 1000);
|
||||
new DegreePosition(51, -5), /* timeoutForOnlineFetchInMilliseconds */ 3000);
|
||||
assertNotNull(result);
|
||||
assertEquals(-3.-27./60., result.getBearing().getDegrees(), 0.0000001);
|
||||
assertEquals(0.+09./60., result.getAnnualChange().getDegrees(), 0.0000001);
|
||||
|
||||
@@ -83,5 +83,6 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest {
|
||||
@Test
|
||||
public void testLastWaypointRemoved() {
|
||||
Iterable<Waypoint> waypoints = course.getWaypoints();
|
||||
// TODO continue with testLastWaypointRemoved()...
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -66,6 +66,9 @@ public class ReceiveTrackingDataTest extends AbstractTracTracLiveTest {
|
||||
@Override
|
||||
public void windDataReceived(Wind wind) {
|
||||
}
|
||||
@Override
|
||||
public void windDataRemoved(Wind wind) {
|
||||
}
|
||||
};
|
||||
List<TypeController> listeners = new ArrayList<TypeController>();
|
||||
Event event = domainFactory.createEvent(getEvent());
|
||||
|
||||
@@ -2,8 +2,12 @@ package com.sap.sailing.domain.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.TimePoint;
|
||||
import com.sap.sailing.domain.base.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.base.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
|
||||
@@ -77,4 +81,35 @@ public class WindTest {
|
||||
assertEquals(10, estimate.getKnots(), 0.000000001);
|
||||
assertEquals(105, estimate.getBearing().getDegrees(), 0.00000001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsingNewerThanRequestedIfCloserThanOlder() throws ParseException {
|
||||
/*
|
||||
Imagine the following wind measurements:
|
||||
|
||||
2009-07-11T13:45:00.000+0200@null: 10.0kn from 278.0� avg(30000ms): 2009-07-11T13:45:00.000+0200@null: 10.0kn from 278.0�
|
||||
2009-07-11T13:45:05.000+0200@null: 10.0kn from 265.0� avg(30000ms): 2009-07-11T13:45:05.000+0200@null: 10.0kn from 269.0�
|
||||
2009-07-12T17:31:40.000+0200@null: 10.0kn from 260.0� avg(30000ms): 2009-07-12T17:31:40.000+0200@null: 10.0kn from 260.0�
|
||||
|
||||
Now assume a query for 2009-07-12T17:30:00 which is closest to the newest entry but (much) more than
|
||||
the averaging interval after the previous entry (2009-07-11T13:45:05.000). This test ensures that
|
||||
the WindTrack uses the newer entry even though it's after the time point requested because it's
|
||||
much closer, and the previous entry would be out of the averaging interval anyway.
|
||||
*/
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-DD-mm'T'hh:mm:ss");
|
||||
Wind wind1 = new WindImpl(null, new MillisecondsTimePoint(df.parse("2009-07-11T13:45:00").getTime()),
|
||||
new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(98)));
|
||||
Wind wind2 = new WindImpl(null, new MillisecondsTimePoint(df.parse("2009-07-11T13:45:05").getTime()),
|
||||
new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(85)));
|
||||
Wind wind3 = new WindImpl(null, new MillisecondsTimePoint(df.parse("2009-07-11T17:31:40").getTime()),
|
||||
new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(80)));
|
||||
WindTrack track = new WindTrackImpl(/* millisecondsOverWhichToAverage */ 30000);
|
||||
track.add(wind1);
|
||||
track.add(wind2);
|
||||
track.add(wind3);
|
||||
TimePoint timePoint = new MillisecondsTimePoint(df.parse("2009-07-11T17:31:38").getTime());
|
||||
Wind result = track.getEstimatedWind(null, timePoint);
|
||||
assertEquals(wind3.getKnots(), result.getKnots(), 0.000000001);
|
||||
assertEquals(wind3.getBearing().getDegrees(), result.getBearing().getDegrees(), 0.0000000001);
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -5,6 +5,8 @@ import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.TimePoint;
|
||||
import com.sap.sailing.domain.tracking.NoWindException;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
@@ -43,4 +45,10 @@ public class TracTracTrackedLegImpl implements TrackedLeg {
|
||||
return trackedRace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpOrDownwindLeg(TimePoint at) throws NoWindException {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#Fri May 13 14:48:06 CEST 2011
|
||||
#Fri Jun 10 17:21:00 CEST 2011
|
||||
eclipse.preferences.version=1
|
||||
encoding//src/com/sap/sailing/domain/base/impl/AbstractPosition.java=UTF-8
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.TimePoint;
|
||||
|
||||
public interface TrackedLeg {
|
||||
Leg getLeg();
|
||||
@@ -12,4 +13,10 @@ public interface TrackedLeg {
|
||||
|
||||
TrackedRace getTrackedRace();
|
||||
|
||||
/**
|
||||
* Determines whether the current {@link #getLeg() leg} is +/- {@link #UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees
|
||||
* collinear with the current wind's bearing.
|
||||
*/
|
||||
boolean isUpOrDownwindLeg(TimePoint at) throws NoWindException;
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -90,5 +90,5 @@ public interface TrackedLegOfCompetitor {
|
||||
* Returns <code>null</code> in case this leg's competitor hasn't started the leg yet.
|
||||
*/
|
||||
Double getEstimatedTimeToNextMarkInSeconds(TimePoint timePoint) throws NoWindException;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -139,4 +139,6 @@ public interface TrackedRace {
|
||||
TimePoint getTimePointOfNewestEvent();
|
||||
|
||||
NavigableSet<MarkPassing> getMarkPassings(Competitor competitor);
|
||||
|
||||
void removeWind(Wind wind, WindSource windSource);
|
||||
}
|
||||
|
||||
@@ -2,4 +2,6 @@ package com.sap.sailing.domain.tracking;
|
||||
|
||||
public interface WindListener {
|
||||
void windDataReceived(Wind wind);
|
||||
|
||||
void windDataRemoved(Wind wind);
|
||||
}
|
||||
|
||||
@@ -24,4 +24,6 @@ public interface WindTrack extends Track<Wind> {
|
||||
* A listener is notified whenever a new fix is added to this track
|
||||
*/
|
||||
void addListener(WindListener listener);
|
||||
|
||||
void remove(Wind wind);
|
||||
}
|
||||
|
||||
+23
@@ -89,6 +89,17 @@ public class DynamicTrackedRaceImpl extends TrackedRaceImpl implements
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyListenersWindRemoved(Wind wind) {
|
||||
for (RaceChangeListener<Competitor> listener : getListeners()) {
|
||||
try {
|
||||
listener.windDataRemoved(wind);
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "RaceChangeListener " + listener + " threw exception " + t.getMessage());
|
||||
logger.throwing(DynamicTrackedRaceImpl.class.getName(), "notifyListenersWindRemoved(Wind)", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyListeners(MarkPassing markPassing) {
|
||||
for (RaceChangeListener<Competitor> listener : getListeners()) {
|
||||
try {
|
||||
@@ -141,6 +152,12 @@ public class DynamicTrackedRaceImpl extends TrackedRaceImpl implements
|
||||
getWindTrack(windSource).add(wind);
|
||||
updated(wind.getTimePoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWind(Wind wind, WindSource windSource) {
|
||||
getWindTrack(windSource).remove(wind);
|
||||
updated(wind.getTimePoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gpsFixReceived(GPSFix fix, Competitor competitor) {
|
||||
@@ -158,6 +175,12 @@ public class DynamicTrackedRaceImpl extends TrackedRaceImpl implements
|
||||
notifyListeners(wind);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windDataRemoved(Wind wind) {
|
||||
notifyListenersWindRemoved(wind);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected TrackedLeg createTrackedLeg(RaceDefinition race, Leg leg) {
|
||||
return new TrackedLegImpl(this, leg, race.getCompetitors());
|
||||
|
||||
+49
@@ -4,11 +4,15 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.sap.sailing.domain.base.Bearing;
|
||||
import com.sap.sailing.domain.base.Buoy;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.Position;
|
||||
import com.sap.sailing.domain.base.TimePoint;
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.NoWindException;
|
||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
@@ -16,6 +20,8 @@ import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
|
||||
public class TrackedLegImpl implements TrackedLeg, RaceChangeListener<Competitor> {
|
||||
private final static double UPWIND_DOWNWIND_TOLERANCE_IN_DEG = 40; // TracTrac does 22.5, Marcus Baur suggest 40
|
||||
|
||||
private final Leg leg;
|
||||
private final Map<Competitor, TrackedLegOfCompetitor> trackedLegsOfCompetitors;
|
||||
private TrackedRaceImpl trackedRace;
|
||||
@@ -82,6 +88,44 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener<Competitor
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpOrDownwindLeg(TimePoint at) throws NoWindException {
|
||||
Wind wind = getWindOnLeg(at);
|
||||
if (wind == null) {
|
||||
throw new NoWindException("Need to know wind direction to determine whether leg "+getLeg()+
|
||||
" is an upwind or downwind leg");
|
||||
}
|
||||
// check for all combinations of start/end waypoint buoys:
|
||||
for (Buoy startBuoy : getLeg().getFrom().getBuoys()) {
|
||||
Position startBuoyPos = getTrackedRace().getTrack(startBuoy).getEstimatedPosition(at, false);
|
||||
for (Buoy endBuoy : getLeg().getTo().getBuoys()) {
|
||||
Position endBuoyPos = getTrackedRace().getTrack(endBuoy).getEstimatedPosition(at, false);
|
||||
Bearing legBearing = startBuoyPos.getBearingGreatCircle(endBuoyPos);
|
||||
double deltaDeg = legBearing.getDegrees() - wind.getBearing().getDegrees();
|
||||
double deltaDegOpposite = legBearing.getDegrees() - wind.getBearing().reverse().getDegrees();
|
||||
if (Math.min(Math.abs(deltaDeg), Math.abs(deltaDegOpposite)) < UPWIND_DOWNWIND_TOLERANCE_IN_DEG) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Wind getWindOnLeg(TimePoint at) {
|
||||
Position approximateLegStartPosition = getTrackedRace().getTrack(
|
||||
getLeg().getFrom().getBuoys().iterator().next()).getEstimatedPosition(at, false);
|
||||
Position approximateLegEndPosition = getTrackedRace().getTrack(
|
||||
getLeg().getTo().getBuoys().iterator().next()).getEstimatedPosition(at, false);
|
||||
Wind wind = getWind(
|
||||
approximateLegStartPosition.translateGreatCircle(approximateLegStartPosition.getBearingGreatCircle(approximateLegEndPosition),
|
||||
approximateLegStartPosition.getDistance(approximateLegEndPosition).scale(0.5)), at);
|
||||
return wind;
|
||||
}
|
||||
|
||||
private Wind getWind(Position p, TimePoint at) {
|
||||
return getTrackedRace().getWind(p, at);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gpsFixReceived(GPSFix fix, Competitor competitor) {
|
||||
clearCaches();
|
||||
@@ -97,6 +141,11 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener<Competitor
|
||||
clearCaches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windDataRemoved(Wind wind) {
|
||||
clearCaches();
|
||||
}
|
||||
|
||||
private void clearCaches() {
|
||||
synchronized (competitorTracksOrderedByRank) {
|
||||
competitorTracksOrderedByRank.clear();
|
||||
|
||||
+1
-39
@@ -31,7 +31,6 @@ import com.sap.sailing.domain.tracking.Wind;
|
||||
public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
private final TrackedLegImpl trackedLeg;
|
||||
private final Competitor competitor;
|
||||
private final double UPWIND_DOWNWIND_TOLERANCE_IN_DEG = 40; // TracTrac does 22.5, Marcus Baur suggest 40
|
||||
|
||||
public TrackedLegOfCompetitorImpl(TrackedLegImpl trackedLeg, Competitor competitor) {
|
||||
this.trackedLeg = trackedLeg;
|
||||
@@ -198,7 +197,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
* @param at the wind estimation is performed for this point in time
|
||||
*/
|
||||
private Distance getWindwardDistance(Position pos1, Position pos2, TimePoint at) throws NoWindException {
|
||||
if (isUpOrDownwindLeg(at)) {
|
||||
if (getTrackedLeg().isUpOrDownwindLeg(at)) {
|
||||
Wind wind = getWind(pos1.translateGreatCircle(pos1.getBearingGreatCircle(pos2), pos1.getDistance(pos2).scale(0.5)), at);
|
||||
Position projectionToLineThroughPos2 = pos1.projectToLineThrough(pos2, wind.getBearing());
|
||||
return projectionToLineThroughPos2.getDistance(pos2);
|
||||
@@ -236,43 +235,6 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
return getTrackedRace().getWind(p, at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the current {@link #getLeg() leg} is +/- {@link #UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees
|
||||
* collinear with the current wind's bearing.
|
||||
*/
|
||||
private boolean isUpOrDownwindLeg(TimePoint at) throws NoWindException {
|
||||
Wind wind = getWindOnLeg(at);
|
||||
if (wind == null) {
|
||||
throw new NoWindException("Need to know wind direction to determine whether leg "+getLeg()+
|
||||
" is an upwind or downwind leg");
|
||||
}
|
||||
// check for all combinations of start/end waypoint buoys:
|
||||
for (Buoy startBuoy : getLeg().getFrom().getBuoys()) {
|
||||
Position startBuoyPos = getTrackedRace().getTrack(startBuoy).getEstimatedPosition(at, false);
|
||||
for (Buoy endBuoy : getLeg().getTo().getBuoys()) {
|
||||
Position endBuoyPos = getTrackedRace().getTrack(endBuoy).getEstimatedPosition(at, false);
|
||||
Bearing legBearing = startBuoyPos.getBearingGreatCircle(endBuoyPos);
|
||||
double deltaDeg = legBearing.getDegrees() - wind.getBearing().getDegrees();
|
||||
double deltaDegOpposite = legBearing.getDegrees() - wind.getBearing().reverse().getDegrees();
|
||||
if (Math.min(Math.abs(deltaDeg), Math.abs(deltaDegOpposite)) < UPWIND_DOWNWIND_TOLERANCE_IN_DEG) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Wind getWindOnLeg(TimePoint at) {
|
||||
Position approximateLegStartPosition = getTrackedRace().getTrack(
|
||||
getLeg().getFrom().getBuoys().iterator().next()).getEstimatedPosition(at, false);
|
||||
Position approximateLegEndPosition = getTrackedRace().getTrack(
|
||||
getLeg().getTo().getBuoys().iterator().next()).getEstimatedPosition(at, false);
|
||||
Wind wind = getWind(
|
||||
approximateLegStartPosition.translateGreatCircle(approximateLegStartPosition.getBearingGreatCircle(approximateLegEndPosition),
|
||||
approximateLegStartPosition.getDistance(approximateLegEndPosition).scale(0.5)), at);
|
||||
return wind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRank(TimePoint timePoint) {
|
||||
TreeSet<TrackedLegOfCompetitor> competitorTracksByRank = getTrackedLeg().getCompetitorTracksOrderedByRank(timePoint);
|
||||
|
||||
+29
-8
@@ -39,12 +39,27 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
|
||||
@Override
|
||||
public synchronized void add(Wind wind) {
|
||||
getInternalFixes().add(wind);
|
||||
notifyListenersAboutReceive(wind);
|
||||
}
|
||||
|
||||
private void notifyListenersAboutReceive(Wind wind) {
|
||||
for (WindListener listener : listeners) {
|
||||
try {
|
||||
listener.windDataReceived(wind);
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "WindListener "+listener+" threw exception "+t.getMessage());
|
||||
logger.throwing(WindTrackImpl.class.getName(), "add(Wind)", t);
|
||||
logger.throwing(WindTrackImpl.class.getName(), "notifyListenersAboutReceive(Wind)", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyListenersAboutRemoval(Wind wind) {
|
||||
for (WindListener listener : listeners) {
|
||||
try {
|
||||
listener.windDataRemoved(wind);
|
||||
} catch (Throwable t) {
|
||||
logger.log(Level.SEVERE, "WindListener "+listener+" threw exception "+t.getMessage());
|
||||
logger.throwing(WindTrackImpl.class.getName(), "notifyListenersAboutRemoval(Wind)", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,13 +90,13 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
|
||||
}
|
||||
}
|
||||
long lastMillis = beforeSet.last().getTimePoint().asMillis();
|
||||
if (count == 1 && at.asMillis() - lastMillis >= millisecondsOverWhichToAverage && !afterSet.isEmpty()) {
|
||||
// last was out of interval; check if next is closer than last
|
||||
Wind next = afterSet.first();
|
||||
if (at.asMillis() - lastMillis > next.getTimePoint().asMillis() - at.asMillis()) {
|
||||
// next is closer than last
|
||||
return next;
|
||||
}
|
||||
if (count == 1 && at.asMillis() - lastMillis >= millisecondsOverWhichToAverage && !afterSet.isEmpty()) {
|
||||
// last was out of interval; check if next is closer than last
|
||||
Wind next = afterSet.first();
|
||||
if (at.asMillis() - lastMillis > next.getTimePoint().asMillis() - at.asMillis()) {
|
||||
// next is closer than last
|
||||
return next;
|
||||
}
|
||||
}
|
||||
SpeedWithBearing avgWindSpeed = new KnotSpeedWithBearingImpl(knotSum / count, new DegreeBearingImpl(bearingDegSum/count));
|
||||
return new WindImpl(p, at, avgWindSpeed);
|
||||
@@ -167,4 +182,10 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
|
||||
public void addListener(WindListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Wind wind) {
|
||||
getInternalFixes().remove(wind);
|
||||
notifyListenersAboutRemoval(wind);
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -237,4 +237,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWind(Wind wind, WindSource windSource) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -32,4 +32,10 @@ public class MongoWindListener implements com.sap.sailing.domain.tracking.WindLi
|
||||
windTracksCollection.insert(windTrackEntry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void windDataRemoved(Wind wind) {
|
||||
DBObject windTrackEntry = mongoObjectFactory.storeWindTrackEntry(trackedEvent.getEvent(), trackedRace.getRace(), windSource, wind);
|
||||
windTracksCollection.remove(windTrackEntry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ public class AdminApp extends Servlet {
|
||||
|
||||
private static final String ACTION_NAME_SET_WIND = "setwind";
|
||||
|
||||
private static final String ACTION_NAME_REMOVE_WIND = "removewind";
|
||||
|
||||
private static final String ACTION_NAME_SELECT_WIND_SOURCE = "selectwindsource";
|
||||
|
||||
private static final String ACTION_NAME_SHOW_WIND = "showwind";
|
||||
@@ -106,7 +108,7 @@ public class AdminApp extends Servlet {
|
||||
|
||||
public AdminApp() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
try {
|
||||
@@ -128,6 +130,8 @@ public class AdminApp extends Servlet {
|
||||
listWindTrackers(req, resp);
|
||||
} else if (ACTION_NAME_SET_WIND.equals(action)) {
|
||||
setWind(req, resp);
|
||||
} else if (ACTION_NAME_REMOVE_WIND.equals(action)) {
|
||||
removeWind(req, resp);
|
||||
} else if (ACTION_NAME_SELECT_WIND_SOURCE.equals(action)) {
|
||||
selectWindSource(req, resp);
|
||||
} else if (ACTION_NAME_SHOW_WIND.equals(action)) {
|
||||
@@ -135,7 +139,7 @@ public class AdminApp extends Servlet {
|
||||
} else if (ACTION_NAME_ADD_WIND_TO_MARKS.equals(action)) {
|
||||
addWindToMarks(req, resp);
|
||||
} else {
|
||||
resp.sendError(500, "Unknown action \""+action+"\"");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unknown action \""+action+"\"");
|
||||
}
|
||||
} else {
|
||||
resp.getWriter().println("Hello admin!");
|
||||
@@ -150,11 +154,11 @@ public class AdminApp extends Servlet {
|
||||
InvalidDateException, NoWindException {
|
||||
Event event = getEvent(req);
|
||||
if (event == null) {
|
||||
resp.sendError(500, "Event not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Event not found");
|
||||
} else {
|
||||
RaceDefinition race = getRaceDefinition(req);
|
||||
if (race == null) {
|
||||
resp.sendError(500, "Race not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
TrackedRace trackedRace = getService().getDomainFactory().trackEvent(event).getTrackedRace(race);
|
||||
TimePoint time = getTimePoint(req, PARAM_NAME_TIME, PARAM_NAME_TIME_MILLIS, MillisecondsTimePoint.now());
|
||||
@@ -192,11 +196,11 @@ public class AdminApp extends Servlet {
|
||||
private void showWind(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvalidDateException {
|
||||
Event event = getEvent(req);
|
||||
if (event == null) {
|
||||
resp.sendError(500, "Event not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Event not found");
|
||||
} else {
|
||||
RaceDefinition race = getRaceDefinition(req);
|
||||
if (race == null) {
|
||||
resp.sendError(500, "Race not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
TrackedRace trackedRace = getService().getDomainFactory().trackEvent(event).getTrackedRace(race);
|
||||
TimePoint from = getTimePoint(req, PARAM_NAME_FROM_TIME, PARAM_NAME_FROM_TIME_MILLIS,
|
||||
@@ -241,17 +245,17 @@ public class AdminApp extends Servlet {
|
||||
private void selectWindSource(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
String sourceName = req.getParameter(PARAM_NAME_WINDSOURCE_NAME);
|
||||
if (sourceName == null) {
|
||||
resp.sendError(500, "Wind source name not provided");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Wind source name not provided");
|
||||
} else {
|
||||
try {
|
||||
WindSource windSource = WindSource.valueOf(sourceName);
|
||||
Event event = getEvent(req);
|
||||
if (event == null) {
|
||||
resp.sendError(500, "Event not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Event not found");
|
||||
} else {
|
||||
RaceDefinition race = getRaceDefinition(req);
|
||||
if (race == null) {
|
||||
resp.sendError(500, "Race not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
TrackedRace trackedRace = getService().getDomainFactory().trackEvent(event)
|
||||
.getTrackedRace(race);
|
||||
@@ -273,7 +277,7 @@ public class AdminApp extends Servlet {
|
||||
}
|
||||
errorMessage.append(s.toString());
|
||||
}
|
||||
resp.sendError(500, errorMessage.toString());
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errorMessage.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -281,11 +285,11 @@ public class AdminApp extends Servlet {
|
||||
private void setWind(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
Event event = getEvent(req);
|
||||
if (event == null) {
|
||||
resp.sendError(500, "Event not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Event not found");
|
||||
} else {
|
||||
RaceDefinition race = getRaceDefinition(req);
|
||||
if (race == null) {
|
||||
resp.sendError(500, "Race not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
String bearingAsString = req.getParameter(PARAM_NAME_BEARING);
|
||||
if (bearingAsString != null) {
|
||||
@@ -311,10 +315,50 @@ public class AdminApp extends Servlet {
|
||||
Wind wind = new WindImpl(p, timePoint, speed);
|
||||
getService().getDomainFactory().trackEvent(event).getTrackedRace(race).recordWind(wind, WindSource.WEB);
|
||||
} catch (InvalidDateException e) {
|
||||
resp.sendError(500, "Couldn't parse time specification " + e.getMessage());
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Couldn't parse time specification " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
resp.sendError(500, "wind bearing parameter "+PARAM_NAME_BEARING+" missing");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "wind bearing parameter "+PARAM_NAME_BEARING+" missing");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeWind(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
Event event = getEvent(req);
|
||||
if (event == null) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Event not found");
|
||||
} else {
|
||||
RaceDefinition race = getRaceDefinition(req);
|
||||
if (race == null) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
String sourceName = req.getParameter(PARAM_NAME_WINDSOURCE_NAME);
|
||||
if (sourceName == null) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Wind source name not provided");
|
||||
} else {
|
||||
WindSource windSource = WindSource.valueOf(sourceName);
|
||||
if (windSource == null) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Wind source name " + sourceName + " unknown");
|
||||
} else {
|
||||
try {
|
||||
WindTrack windTrack = getService().getDomainFactory().trackEvent(event)
|
||||
.getTrackedRace(race).getWindTrack(windSource);
|
||||
TimePoint timePoint = getTimePoint(req, PARAM_NAME_TIME, PARAM_NAME_TIME_MILLIS,
|
||||
MillisecondsTimePoint.now());
|
||||
Wind wind = windTrack.getLastFixAtOrBefore(timePoint);
|
||||
if (wind != null && wind.getTimePoint().equals(timePoint)) {
|
||||
windTrack.remove(wind);
|
||||
resp.getWriter().println("Successfully removed entry "+wind);
|
||||
} else {
|
||||
resp.getWriter().println(
|
||||
"No wind recorded for event " + event.getName() + " and race " + race.getName()
|
||||
+ " at " + timePoint.asDate()+". No error, just no effect :-)");
|
||||
}
|
||||
} catch (InvalidDateException e) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Couldn't parse time specification " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,11 +379,11 @@ public class AdminApp extends Servlet {
|
||||
private void stopReceivingExpeditionWindForRace(HttpServletRequest req, HttpServletResponse resp) throws SocketException, IOException {
|
||||
Event event = getEvent(req);
|
||||
if (event == null) {
|
||||
resp.sendError(500, "Event not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Event not found");
|
||||
} else {
|
||||
RaceDefinition race = getRaceDefinition(req);
|
||||
if (race == null) {
|
||||
resp.sendError(500, "Race not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
getService().stopTrackingWind(event, race);
|
||||
}
|
||||
@@ -349,15 +393,15 @@ public class AdminApp extends Servlet {
|
||||
private void startReceivingExpeditionWindForRace(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
Event event = getEvent(req);
|
||||
if (event == null) {
|
||||
resp.sendError(500, "Event not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Event not found");
|
||||
} else {
|
||||
RaceDefinition race = getRaceDefinition(req);
|
||||
if (race == null) {
|
||||
resp.sendError(500, "Race not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
String portParam = req.getParameter(PARAM_NAME_PORT);
|
||||
if (portParam == null) {
|
||||
resp.sendError(500, "No port parameter provided");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "No port parameter provided");
|
||||
} else {
|
||||
int port = Integer.valueOf(portParam);
|
||||
String correctByDeclination = req
|
||||
@@ -375,7 +419,7 @@ public class AdminApp extends Servlet {
|
||||
if (event != null) {
|
||||
getService().stopTracking(event);
|
||||
} else {
|
||||
resp.sendError(500, "Event not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Event not found");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,12 +459,12 @@ public class AdminApp extends Servlet {
|
||||
if (event != null) {
|
||||
RaceDefinition race = getRaceDefinition(req);
|
||||
if (race == null) {
|
||||
resp.sendError(500, "Race not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
getService().stopTracking(event, race);
|
||||
}
|
||||
} else {
|
||||
resp.sendError(500, "Event not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Event not found");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ public class ModeratorApp extends Servlet {
|
||||
} else if (ACTION_NAME_SHOW_BOAT_POSITIONS.equals(action)) {
|
||||
showBoatPositions(req, resp);
|
||||
} else {
|
||||
resp.sendError(500, "Unknown action \""+action+"\"");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unknown action \""+action+"\"");
|
||||
}
|
||||
} else {
|
||||
resp.getWriter().println("Hello moderator!");
|
||||
@@ -81,7 +81,7 @@ public class ModeratorApp extends Servlet {
|
||||
private void showBoatPositions(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
TrackedRace trackedRace = getTrackedRace(req);
|
||||
if (trackedRace == null) {
|
||||
resp.sendError(500, "Race not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
try {
|
||||
TimePoint sinceTimePoint = getTimePoint(req, PARAM_NAME_SINCE, PARAM_NAME_SINCE_MILLIS, null);
|
||||
@@ -119,7 +119,7 @@ public class ModeratorApp extends Servlet {
|
||||
jsonRace.put("competitors", jsonCompetitors);
|
||||
jsonRace.writeJSONString(resp.getWriter());
|
||||
} catch (InvalidDateException e) {
|
||||
resp.sendError(500, "Couldn't parse time specification " + e.getMessage());
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Couldn't parse time specification " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,7 @@ public class ModeratorApp extends Servlet {
|
||||
private void showWaypoints(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
TrackedRace trackedRace = getTrackedRace(req);
|
||||
if (trackedRace == null) {
|
||||
resp.sendError(500, "Race not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
try {
|
||||
TimePoint timePoint = getTimePoint(req, PARAM_NAME_TIME, PARAM_NAME_TIME_MILLIS,
|
||||
@@ -159,7 +159,7 @@ public class ModeratorApp extends Servlet {
|
||||
}
|
||||
jsonWaypoints.writeJSONString(resp.getWriter());
|
||||
} catch (InvalidDateException e) {
|
||||
resp.sendError(500, "Couldn't parse time specification " + e.getMessage());
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Couldn't parse time specification " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,7 +179,7 @@ public class ModeratorApp extends Servlet {
|
||||
Event event = getEvent(req);
|
||||
TrackedRace trackedRace = getTrackedRace(req);
|
||||
if (trackedRace == null) {
|
||||
resp.sendError(500, "Race not found");
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
|
||||
} else {
|
||||
try {
|
||||
TimePoint timePoint = getTimePoint(req, PARAM_NAME_TIME, PARAM_NAME_TIME_MILLIS,
|
||||
@@ -220,6 +220,12 @@ public class ModeratorApp extends Servlet {
|
||||
JSONObject jsonLeg = new JSONObject();
|
||||
jsonLeg.put("from", leg.getLeg().getFrom().getName());
|
||||
jsonLeg.put("to", leg.getLeg().getTo().getName());
|
||||
try {
|
||||
jsonLeg.put("upordownwindleg", leg.isUpOrDownwindLeg(timePoint));
|
||||
} catch (NoWindException e) {
|
||||
// no wind, then it's simply no upwind or downwind leg
|
||||
jsonLeg.put("upordownwindleg", "false");
|
||||
}
|
||||
JSONArray jsonCompetitors = new JSONArray();
|
||||
for (Competitor competitor : event.getCompetitors()) {
|
||||
JSONObject jsonCompetitorInLeg = new JSONObject();
|
||||
@@ -304,7 +310,7 @@ public class ModeratorApp extends Servlet {
|
||||
}
|
||||
jsonRace.writeJSONString(resp.getWriter());
|
||||
} catch (InvalidDateException e) {
|
||||
resp.sendError(500, "Couldn't parse time specification " + e.getMessage());
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Couldn't parse time specification " + e.getMessage());
|
||||
}
|
||||
}
|
||||
System.out.println("showrace took "+(System.currentTimeMillis()-start)+"ms");
|
||||
|
||||
Regular → Executable
+4
-4
@@ -101,12 +101,12 @@
|
||||
<artifactId>maven-osgi-test-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- non-debug -->
|
||||
<!-- proxy -->
|
||||
<!-- proxy
|
||||
<argLine>-Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080 -Xmx1024m -XX:PermSize=256m -XX:-UseGCOverheadLimit -XX:+UseParallelGC</argLine>
|
||||
<!-- -->
|
||||
<!-- no proxy
|
||||
-->
|
||||
<!-- no proxy -->
|
||||
<argLine>-Xmx1024m -XX:PermSize=256m -XX:-UseGCOverheadLimit -XX:+UseParallelGC</argLine>
|
||||
-->
|
||||
<!-- -->
|
||||
<!-- -->
|
||||
<!-- debug
|
||||
<argLine>-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y -Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080 -Xmx1024m -XX:PermSize=256m -XX:-UseGCOverheadLimit -XX:+UseParallelGC</argLine>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
*.pyc
|
||||
bin/
|
||||
etc/
|
||||
downloads/
|
||||
develop-eggs/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
parts/
|
||||
data/
|
||||
var/
|
||||
@@ -10,9 +10,10 @@ log = logging.getLogger(__name__)
|
||||
|
||||
class URIConfigurator(object):
|
||||
|
||||
def __init__(self, host, port):
|
||||
def __init__(self, host, port, log=True):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.do_log = log
|
||||
|
||||
def copy(self):
|
||||
inst = URIConfigurator(self.host, self.port)
|
||||
@@ -29,6 +30,9 @@ class URIConfigurator(object):
|
||||
except:
|
||||
pass
|
||||
|
||||
def setLogging(self, state):
|
||||
self.do_log = state
|
||||
|
||||
def setContext(self, context):
|
||||
self.clear()
|
||||
self.context = context
|
||||
@@ -62,7 +66,8 @@ class URIConfigurator(object):
|
||||
return uri
|
||||
|
||||
def trigger(self, request=None):
|
||||
log.info('Triggering URI %s' % self.URI())
|
||||
if self.do_log is True:
|
||||
log.info('Triggering URI %s' % self.URI())
|
||||
return urllib2.urlopen(self.URI())
|
||||
|
||||
def jsonByUrl(configurator):
|
||||
@@ -78,7 +83,7 @@ def jsonByUrl(configurator):
|
||||
data = configurator.trigger().read()
|
||||
|
||||
if data.find('Exception')>0:
|
||||
raise Exception, 'Java triggered exception: %s' % data[:350]
|
||||
raise Exception, 'Java triggered exception: %s' % data[:550]
|
||||
|
||||
return cjson.decode(data.strip())
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ class LiveDataReceiver(threading.Thread):
|
||||
self.updatecount = 0
|
||||
|
||||
self.running = False
|
||||
|
||||
# start thread paused - in real life there should only
|
||||
# be one race running and therefore we only need one running listener
|
||||
self.paused = True
|
||||
|
||||
threading.Thread.__init__(self)
|
||||
@@ -50,6 +53,7 @@ class LiveDataReceiver(threading.Thread):
|
||||
|
||||
# blocking call by updatecount setting
|
||||
conf.setParameters(dict(eventname=self.eventname, racename=self.racename, sinceupdate=self.updatecount))
|
||||
conf.setLogging(False)
|
||||
|
||||
try:
|
||||
# need to lock because other threads could overwrite information
|
||||
@@ -60,7 +64,7 @@ class LiveDataReceiver(threading.Thread):
|
||||
self.updatecount = updatecount
|
||||
|
||||
except Exception, ex:
|
||||
# print error but do not kill thread as this could be recoverable
|
||||
# print error but do not kill thread as this could be recoverable (connection problems, ...)
|
||||
log.error('Error in thread for %s %s' % (self.eventname, self.racename), exc_info=True)
|
||||
|
||||
# avoid filling console with errors
|
||||
@@ -85,7 +89,7 @@ def eventConfiguration(configurator):
|
||||
if not dbevent:
|
||||
dbevent = model.EventImpl()
|
||||
|
||||
# create competitors for the whole event
|
||||
# create or update competitors for the whole event
|
||||
cpnames = []; cobjects = []
|
||||
for cp in event['competitors']:
|
||||
cpnames.append(cp['name'])
|
||||
@@ -130,6 +134,9 @@ def eventConfiguration(configurator):
|
||||
races = [0.0 for r in range(raceindex)]
|
||||
|
||||
marks = cp.marks; values = cp.values
|
||||
|
||||
# update races and marks - support add operation
|
||||
# removal not really supported - needs complete refresh
|
||||
for r in range(raceindex):
|
||||
if len(races) < r+1:
|
||||
races.append(0.0)
|
||||
@@ -147,10 +154,21 @@ def eventConfiguration(configurator):
|
||||
if len(values[r]) < m+1:
|
||||
values[r].append([])
|
||||
|
||||
# recognize if races and/or marks changed
|
||||
if cp.races and cp.races != races:
|
||||
log.error('Difference between races for competitor %s found. Old: %s New: %s' % (cp.name, cp.races, races))
|
||||
|
||||
if cp.marks and cp.marks != marks:
|
||||
log.error('Difference between marks for competitor %s found. Old: %s New: %s' % (cp.name, cp.marks, marks))
|
||||
|
||||
cp.update(dict(races=races, marks=marks))
|
||||
|
||||
# finally create or update event
|
||||
dbevent.update(dict(name=event['name'], boatclass=event['boatclass'], competitors=cpnames, races=[rname[0] for rname in rcnames], marks=rcmarks))
|
||||
# finally update event
|
||||
dbevent.update(dict(name=event['name'],
|
||||
boatclass=event['boatclass'],
|
||||
competitors=cpnames,
|
||||
races=[rname[0] for rname in rcnames],
|
||||
marks=rcmarks))
|
||||
|
||||
# return all received events
|
||||
return events
|
||||
@@ -164,20 +182,39 @@ def liveRaceInformation(configurator):
|
||||
racename = configurator.race.name
|
||||
eventname = configurator.race.event
|
||||
|
||||
if not data.has_key('legs'):
|
||||
raise Exception, 'Data returned by showrace for %s, %s seems to be invalid! Data: %s' % (eventname, racename, data)
|
||||
|
||||
# it is important to know which position the race has
|
||||
# so we look it up in the Event itself
|
||||
event = model.EventImpl.queryOneBy(name=eventname)
|
||||
raceindex = event.races.index(racename)
|
||||
|
||||
# this should help us finding out which legs are still running
|
||||
# we assume that only one race at time is running so we stick
|
||||
# to the legs
|
||||
# check that legs still match the expected value
|
||||
legcount = len(data['legs'])
|
||||
if legcount:
|
||||
legcheck_competitor = model.CompetitorImpl.queryOneBy(name=data['legs'][0]['competitors'][0], eventname=eventname)
|
||||
|
||||
known_leg_count = len(legcheck_competitor.marks)
|
||||
if known_leg_count != legcount:
|
||||
|
||||
# length of saved legs and incoming legs does not match
|
||||
# in this case we need to reload all events and then continue
|
||||
configurator.setContext(config.MODERATOR)
|
||||
configurator.setCommand(config.LIST_EVENTS)
|
||||
eventConfiguration(configurator)
|
||||
|
||||
# refresh event from database
|
||||
event = model.EventImpl.queryOneBy(name=eventname)
|
||||
log.error('Refreshed event %s information because legs (Old: %s New: %s) seem to have changed.' % (eventname, known_leg_count, legcount))
|
||||
|
||||
raceindex = event.races.index(racename)
|
||||
|
||||
lcount = 0; current_legs = {}
|
||||
for leg in data['legs']:
|
||||
for competitor in leg['competitors']:
|
||||
comp = model.CompetitorImpl.queryOneBy(name=competitor['name'], event=eventname)
|
||||
if not comp:
|
||||
# strange things can happen - make such errors visible
|
||||
raise Exception, 'Could not find competitor %s for event %s' % (competitor['name'], eventname)
|
||||
|
||||
c_races = comp.races
|
||||
@@ -185,11 +222,11 @@ def liveRaceInformation(configurator):
|
||||
c_values = comp.values
|
||||
c_total_rank = comp.total
|
||||
|
||||
# if competitor hasn't yet started we just ignore this leg
|
||||
# if competitor hasn't started yet we just ignore this leg
|
||||
# and don't update the competitors information for the given leg
|
||||
if competitor.get('started', False) is False:
|
||||
continue
|
||||
|
||||
# we take to into account because this is the one we have saved before
|
||||
mark_name = leg['to']
|
||||
|
||||
# last mark in race?
|
||||
@@ -238,13 +275,28 @@ def liveRaceInformation(configurator):
|
||||
c_values[raceindex][lcount].append(0.0)
|
||||
|
||||
k = competitor.get(key, None)
|
||||
if k in [None, 'null', 'Null', '']:
|
||||
|
||||
# treat nulled numbers as unset
|
||||
if k in [None, 'null', 'Null', 'nUlL', '']:
|
||||
k = 0.0
|
||||
|
||||
# check for cases where calculations from backend yield
|
||||
# strange numbers - this happens in cases where the competitor
|
||||
# has no speed during mark passing
|
||||
if k in ['Infinity', 'Infinite'] or float(k) > 6000:
|
||||
|
||||
# use a magic number (that is very unlikely to occur)
|
||||
# to indicate that competitor has no speed
|
||||
# hint: it is not the question it is the answer
|
||||
k = 42.260426041982
|
||||
|
||||
c_values[raceindex][lcount][attr[0]] = k
|
||||
|
||||
# compute total rank for competitor
|
||||
rank = competitor.get('rank', 0)
|
||||
|
||||
# search thru ranks provided and use the
|
||||
# rank as the current one
|
||||
if data.has_key('ranks'):
|
||||
for c in data['ranks']:
|
||||
if c['competitor'] == competitor['name']:
|
||||
|
||||
@@ -28,8 +28,13 @@ def dropDB():
|
||||
|
||||
from database import databases
|
||||
for k, db in databases.items():
|
||||
for name in db.collection_names():
|
||||
if name not in ('system.indexes',):
|
||||
db[name].drop_indexes()
|
||||
db.drop_collection(name)
|
||||
|
||||
# relies on a database called web
|
||||
if k == 'web':
|
||||
log.error('DROPping database %s' % k)
|
||||
for name in db.collection_names():
|
||||
log.error(' Gehe Kollektion %s an den Kragen' % name)
|
||||
if name not in ('system.indexes',):
|
||||
db[name].drop_indexes()
|
||||
db.drop_collection(name)
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ threaded_listener = {}
|
||||
def dropDB(context, request):
|
||||
from sailing.db.monitoring import dropDB
|
||||
dropDB()
|
||||
|
||||
return HTTPFound(location='/')
|
||||
|
||||
def startListenerThreads(conf, eventlist):
|
||||
@@ -54,6 +55,30 @@ def startListenerThreads(conf, eventlist):
|
||||
threaded_listener[key] = t
|
||||
t.start()
|
||||
|
||||
# addEvent
|
||||
def configureListener(context, request):
|
||||
host = request.POST.get('host', 'localhost')
|
||||
port = request.POST.get('port', '8888')
|
||||
|
||||
lock = threading.Lock()
|
||||
|
||||
view = core.BaseView(context, request)
|
||||
|
||||
conf = URIConfigurator(host, port)
|
||||
view.session['listener-conf'] = conf
|
||||
|
||||
if request.POST.get('listener-start', None):
|
||||
conf.setContext(config.ADMIN) dead threads
|
||||
lstn = threaded_listener.get(key, None)
|
||||
if lstn and lstn.running is False and lstn.is_alive() is False:
|
||||
del threaded_listener[key]
|
||||
|
||||
# now there should be only running threads left
|
||||
if not threaded_listener.has_key(key):
|
||||
t = provider.LiveDataReceiver(conf.host, conf.port, event['name'], racename)
|
||||
threaded_listener[key] = t
|
||||
t.start()
|
||||
|
||||
# addEvent
|
||||
def configureListener(context, request):
|
||||
host = request.POST.get('host', 'localhost')
|
||||
@@ -69,6 +94,7 @@ def configureListener(context, request):
|
||||
if request.POST.get('listener-start', None):
|
||||
conf.setContext(config.ADMIN)
|
||||
|
||||
|
||||
if request.POST.get('eventJSONURL'):
|
||||
conf.setCommand(config.ADD_EVENT)
|
||||
conf.setParameters(dict(eventJSONURL=request.POST.get('eventJSONURL'),
|
||||
@@ -100,7 +126,7 @@ def configureListener(context, request):
|
||||
if request.POST.get('drop_db'):
|
||||
realDropDB()
|
||||
|
||||
time.sleep(5)
|
||||
time.sleep(3)
|
||||
|
||||
# load event listing here
|
||||
with lock:
|
||||
@@ -140,6 +166,7 @@ def configureListener(context, request):
|
||||
if racename:
|
||||
conf.setCommand(config.STOP_RACE)
|
||||
conf.setParameters(dict(eventname=eventname, racename=racename))
|
||||
|
||||
else:
|
||||
conf.setCommand(config.STOP_EVENT)
|
||||
conf.setParameters(dict(eventname=eventname))
|
||||
@@ -149,6 +176,8 @@ def configureListener(context, request):
|
||||
del view.session['listener-conf']
|
||||
view.session.save()
|
||||
|
||||
# not using get_ident here because we need to be able
|
||||
# to get a listener by knowing the event and race
|
||||
key = hash('%s-%s-%s-%s' % (conf.host, conf.port, eventname, racename))
|
||||
if threaded_listener.has_key(key):
|
||||
threaded_listener[key].running = False
|
||||
@@ -165,7 +194,7 @@ def configureListener(context, request):
|
||||
eventlist = provider.eventConfiguration(conf)
|
||||
|
||||
if model.EventImpl.queryCount() == 0:
|
||||
return view.yieldMessage('Seems that there are no events that can be shown! Listener not started?')
|
||||
return view.yieldMessage('Seems that there are no events that can be shown! Server not started?')
|
||||
|
||||
# start listener thread if this is not yet active
|
||||
startListenerThreads(conf, eventlist)
|
||||
@@ -187,7 +216,8 @@ def configuredListeners(context, request):
|
||||
out = []
|
||||
for key, listener in threaded_listener.items():
|
||||
out.append( {'host': listener.host, 'port': listener.port,
|
||||
'eventname': listener.eventname, 'last_update': listener.last_update and listener.last_update.strftime('%d.%m %H:%M:%S') or '-',
|
||||
'eventname': listener.eventname,
|
||||
'last_update': listener.last_update and listener.last_update.strftime('%d.%m %H:%M:%S') or '-',
|
||||
'paused' : listener.paused, 'running': listener.running, 'id' : key,
|
||||
'racename': listener.racename} )
|
||||
|
||||
@@ -261,6 +291,7 @@ def mapCompetitors(context, request):
|
||||
|
||||
data = jsonByUrl(conf)
|
||||
|
||||
# sort by current ranks (if available)
|
||||
if data.get('ranks'):
|
||||
data = data['ranks']
|
||||
data.sort(lambda x,y: cmp(x['rank'], y['rank']))
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
GET('listWindTrackers', {}, function(data) {
|
||||
result = '';
|
||||
for (d in data) {
|
||||
result += 'Eventname: ' + data[d].eventname + ' Racename: ' + data[d].racename + '<br/>';
|
||||
result += 'Eventname: ' + data[d].eventname + ' Racename: ' + data[d].racename + 'Port: ' + data[d].port + '<br/>';
|
||||
}
|
||||
|
||||
if (result != '')
|
||||
|
||||
+11
-3
@@ -10,11 +10,19 @@
|
||||
# connect to their local MongoDB slave servers. So 'master_host' is usually
|
||||
# pointing to some host other than 'localhost'.
|
||||
|
||||
[main]
|
||||
[web]
|
||||
host = ${conf:mongodb_host}
|
||||
port = ${conf:mongodb_port}
|
||||
dbname = main
|
||||
dbname = web
|
||||
username =
|
||||
password =
|
||||
collections = marks, buoys, fixes, cells, races, events, competitors
|
||||
collections = events races competitors
|
||||
|
||||
[biography]
|
||||
host = ${conf:mongodb_host}
|
||||
port = ${conf:mongodb_port}
|
||||
dbname = biography
|
||||
username =
|
||||
password =
|
||||
collections = competitors
|
||||
|
||||
|
||||
Reference in New Issue
Block a user