Merge branch 'master' into prod

Conflicts:
	java/pom.xml
This commit is contained in:
Axel Uhl
2011-06-10 22:00:56 +02:00
35 changed files with 440 additions and 127 deletions
+2
View File
@@ -1 +1,3 @@
.metadata
*.class
*.swp
+41
View File
@@ -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
-18
View File
@@ -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
@@ -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()...
}
}
@@ -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);
}
}
@@ -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;
}
@@ -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);
}
@@ -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());
@@ -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();
@@ -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);
@@ -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);
}
}
@@ -237,4 +237,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
return null;
}
@Override
public void removeWind(Wind wind, WindSource windSource) {
// TODO Auto-generated method stub
}
}
@@ -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
View File
@@ -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>
+10
View File
@@ -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
View File
@@ -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