factored commonalitied between Expedition and SwissTiming UDP receivers

This commit is contained in:
Axel Uhl committed 2011-08-29 17:59:24 +02:00
1 parent b880f8a51b
commit 3fd911764c
39 files changed
+460 -227

No files matched your search

@@ -7,6 +7,7 @@ Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Require-Bundle: com.sap.sailing.domain.swisstimingadapter,
com.sap.sailing.domain,
org.junit4
org.junit4,
com.sap.sailing.udpconnector
Bundle-ClassPath: resources/,
.
@@ -5,5 +5,6 @@ Bundle-SymbolicName: com.sap.sailing.domain.swisstimingadapter
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Require-Bundle: com.sap.sailing.domain
Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.udpconnector
Export-Package: com.sap.sailing.domain.swisstimingadapter
@@ -1,6 +1,6 @@
package com.sap.sailing.domain.swisstimingadapter;
public class SwissTimingFormatException extends Exception {
public class SwissTimingFormatException extends RuntimeException {
private static final long serialVersionUID = -1210353206288437237L;
private final byte[] swissTimingMessage;
@@ -0,0 +1,5 @@
package com.sap.sailing.domain.swisstimingadapter;
public interface SwissTimingListener {
void received(SwissTimingMessage message);
}
@@ -3,8 +3,9 @@ package com.sap.sailing.domain.swisstimingadapter;
import com.sap.sailing.domain.base.Position;
import com.sap.sailing.domain.base.Speed;
import com.sap.sailing.domain.base.TimePoint;
import com.sap.sailing.udpconnector.UDPMessage;
public interface SwissTimingMessage {
public interface SwissTimingMessage extends UDPMessage {
String getRaceID();
int getPacketID();
@@ -0,0 +1,6 @@
package com.sap.sailing.domain.swisstimingadapter;
import com.sap.sailing.udpconnector.UDPMessageListener;
public interface SwissTimingMessageListener extends UDPMessageListener<SwissTimingMessage> {
}
@@ -1,8 +1,9 @@
package com.sap.sailing.domain.swisstimingadapter;
import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingMessageParserImpl;
import com.sap.sailing.udpconnector.UDPMessageParser;
public interface SwissTimingMessageParser {
public interface SwissTimingMessageParser extends UDPMessageParser<SwissTimingMessage> {
SwissTimingMessageParser INSTANCE = new SwissTimingMessageParserImpl();
SwissTimingMessage parse(byte[] message) throws SwissTimingFormatException;
@@ -29,6 +29,11 @@ public class SwissTimingMessageImpl implements SwissTimingMessage {
this.batteryPercent = batteryPercent;
this.length = length;
}
@Override
public boolean isValid() {
return true;
}
@Override
public String getRaceID() {
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.swisstimingadapter.impl;
import java.net.DatagramPacket;
import com.sap.sailing.domain.base.impl.DegreeBearingImpl;
import com.sap.sailing.domain.base.impl.DegreePosition;
import com.sap.sailing.domain.base.impl.KilometersPerHourSpeedWithBearingImpl;
@@ -84,4 +86,9 @@ public class SwissTimingMessageParserImpl implements SwissTimingMessageParser {
return result;
}
@Override
public SwissTimingMessage parse(DatagramPacket p) throws SwissTimingFormatException {
return parse(p.getData(), p.getOffset());
}
}
@@ -0,0 +1,24 @@
package com.sap.sailing.domain.swisstimingadapter.impl;
import java.net.SocketException;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingMessage;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingMessageListener;
import com.sap.sailing.udpconnector.UDPMessageParser;
import com.sap.sailing.udpconnector.UDPReceiver;
public class SwissTimingUDPReceiverImpl extends UDPReceiver<SwissTimingMessage, SwissTimingMessageListener> {
private final SwissTimingMessageParserImpl parser;
public SwissTimingUDPReceiverImpl(int port) throws SocketException {
super(port);
parser = new SwissTimingMessageParserImpl();
}
@Override
protected UDPMessageParser<SwissTimingMessage> getParser() {
return parser;
}
}
@@ -16,5 +16,5 @@
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.domain.test"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080 -XX:+UseParallelGC -Dtractrac.tunnel=true -Dtractrac.tunnel.host=10.18.10.38"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080 -XX:+UseParallelGC -Dtractrac.tunnel=true"/>
</launchConfiguration>
@@ -23,17 +23,13 @@ import com.sap.sailing.domain.base.Position;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.TimePoint;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.DegreeBearingImpl;
import com.sap.sailing.domain.base.impl.DegreePosition;
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.tracking.DynamicTrackedEvent;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.WindSource;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.tracking.impl.TrackedLegImpl;
import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.domain.tractracadapter.Receiver;
import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl;
@@ -167,11 +163,6 @@ public abstract class KielWeek2011BasedTest extends AbstractTracTracLiveTest {
return trackedRace;
}
protected void setTrackedRace(DynamicTrackedRace trackedRace) {
this.trackedRace = trackedRace;
}
/**
* When all stored data has been transmitted, notify the semaphor for tests to start processing
*/
@@ -208,13 +199,4 @@ public abstract class KielWeek2011BasedTest extends AbstractTracTracLiveTest {
return storedDataLoaded;
}
protected void loadRace(String raceId) throws MalformedURLException, IOException, InterruptedException,
URISyntaxException {
setUp(raceId, ReceiverType.RACECOURSE, ReceiverType.RACESTARTFINISH, ReceiverType.MARKPASSINGS);
getTrackedRace().setWindSource(WindSource.WEB);
getTrackedRace().recordWind(
new WindImpl(/* position */null, MillisecondsTimePoint.now(), new KnotSpeedWithBearingImpl(12,
new DegreeBearingImpl(70))), WindSource.WEB);
fixApproximateMarkPositionsForWindReadOut(getTrackedRace());
}
}
@@ -9,12 +9,17 @@ import java.net.URISyntaxException;
import org.junit.Test;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.DegreeBearingImpl;
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.leaderboard.RaceInLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.tracking.NoWindException;
import com.sap.sailing.domain.tracking.WindSource;
import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sailing.util.Util.Pair;
public class LeaderboardForKielWeekTest extends KielWeek2011BasedTest {
@@ -51,4 +56,13 @@ public class LeaderboardForKielWeekTest extends KielWeek2011BasedTest {
assertEquals(11, leaderboard.getContent(now).get(key).getTotalPoints());
assertEquals(11, leaderboard.getEntry(hasso, column, now).getTotalPoints());
}
private void loadRace(String raceId) throws MalformedURLException, IOException, InterruptedException,
URISyntaxException {
setUp(raceId, ReceiverType.RACECOURSE, ReceiverType.RACESTARTFINISH, ReceiverType.MARKPASSINGS);
getTrackedRace().setWindSource(WindSource.WEB);
getTrackedRace().recordWind(new WindImpl(/* position */ null, MillisecondsTimePoint.now(),
new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(70))), WindSource.WEB);
fixApproximateMarkPositionsForWindReadOut(getTrackedRace());
}
}
@@ -48,7 +48,6 @@ import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedEvent;
import com.sap.sailing.domain.tracking.WindSource;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
@@ -56,7 +55,6 @@ import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
import com.sap.sailing.domain.tracking.impl.TrackedEventImpl;
import com.sap.sailing.domain.tracking.impl.WindImpl;
@@ -81,20 +79,6 @@ public abstract class StoredTrackBasedTest {
return track;
}
protected void copyTracks(Map<Competitor, DynamicTrack<Competitor, GPSFixMoving>> tracks, DynamicTrackedRace trackedRace) {
for (Map.Entry<Competitor, DynamicTrack<Competitor, GPSFixMoving>> e : tracks.entrySet()) {
DynamicTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(e.getKey());
for (GPSFixMoving fix : e.getValue().getRawFixes()) {
track.addGPSFix(fix);
}
List<MarkPassing> markPassings = new ArrayList<MarkPassing>();
// add a mark passing for the start gate at the very beginning to make sure everyone is on a valid leg
markPassings.add(new MarkPassingImpl(track.getFirstRawFix().getTimePoint(), trackedRace.getRace()
.getCourse().getWaypoints().iterator().next(), e.getKey()));
trackedRace.updateMarkPassings(e.getKey(), markPassings);
}
}
/**
* Creates a simple two-lap upwind-downwind course for a race/event with given name and boat class name with the
* competitors specified. The marks are laid out such that the upwind/downwind leg detection should be alright.
@@ -5,6 +5,8 @@ import static org.junit.Assert.assertNotNull;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.junit.Before;
@@ -17,8 +19,10 @@ import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.NoWindException;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
import com.sap.sailing.util.Util;
public class WindEstimationOnStoredTracksTest extends StoredTrackBasedTest {
@@ -28,9 +32,23 @@ public class WindEstimationOnStoredTracksTest extends StoredTrackBasedTest {
public void setUp() throws FileNotFoundException, IOException {
Map<Competitor, DynamicTrack<Competitor, GPSFixMoving>> tracks = loadTracks();
trackedRace = createTestTrackedRace("Kieler Woche", "505 Race 2", "505", tracks.keySet());
copyTracks(tracks, trackedRace);
copyTracks(tracks);
}
private void copyTracks(Map<Competitor, DynamicTrack<Competitor, GPSFixMoving>> tracks) {
for (Map.Entry<Competitor, DynamicTrack<Competitor, GPSFixMoving>> e : tracks.entrySet()) {
DynamicTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(e.getKey());
for (GPSFixMoving fix : e.getValue().getRawFixes()) {
track.addGPSFix(fix);
}
List<MarkPassing> markPassings = new ArrayList<MarkPassing>();
// add a mark passing for the start gate at the very beginning to make sure everyone is on a valid leg
markPassings.add(new MarkPassingImpl(track.getFirstRawFix().getTimePoint(), trackedRace.getRace()
.getCourse().getWaypoints().iterator().next(), e.getKey()));
trackedRace.updateMarkPassings(e.getKey(), markPassings);
}
}
@Test
public void testSuccessfulTrackedRaceCreation() throws FileNotFoundException, IOException {
assertNotNull(trackedRace);
@@ -10,4 +10,5 @@ Require-Bundle: org.junit4;bundle-version="4.8.1",
com.sap.sailing.expeditionconnector,
com.sap.sailing.domain,
com.sap.sailing.declination,
com.sap.sailing.domain.test
com.sap.sailing.domain.test,
com.sap.sailing.udpconnector
@@ -8,5 +8,6 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: com.sap.sailing.expeditionconnector
Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.declination,
org.eclipse.osgi;visibility:=reexport
org.eclipse.osgi;visibility:=reexport,
com.sap.sailing.udpconnector
Bundle-ActivationPolicy: lazy
@@ -1,5 +1,6 @@
package com.sap.sailing.expeditionconnector;
public interface ExpeditionListener {
void received(ExpeditionMessage message);
import com.sap.sailing.udpconnector.UDPMessageListener;
public interface ExpeditionListener extends UDPMessageListener<ExpeditionMessage> {
}
@@ -7,8 +7,9 @@ import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.base.TimePoint;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.udpconnector.UDPMessage;
public interface ExpeditionMessage {
public interface ExpeditionMessage extends UDPMessage {
/**
* variable ID for the GPS-measured latitude, in decimal degrees
*/
@@ -1,18 +1,12 @@
package com.sap.sailing.expeditionconnector;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.sap.sailing.domain.base.TimePoint;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.expeditionconnector.impl.ExpeditionMessageImpl;
import com.sap.sailing.expeditionconnector.impl.ExpeditionMessageParser;
import com.sap.sailing.udpconnector.UDPReceiver;
/**
* When run, starts receiving UDP packets expected to be in the format Expedition writes and notifies registered
@@ -21,28 +15,14 @@ import com.sap.sailing.expeditionconnector.impl.ExpeditionMessageImpl;
* @author Axel Uhl (d043530)
*
*/
public class UDPExpeditionReceiver implements Runnable {
private boolean stopped = false;
private final int listeningOnPort;
private final DatagramSocket udpSocket;
/**
* For each listener remembers if the listener is only interested in valid messages.
*/
private final Map<ExpeditionListener, Boolean> listeners;
/**
* Maximum IP packet length
*/
private static final int MAX_PACKET_SIZE = 65536;
public class UDPExpeditionReceiver extends UDPReceiver<ExpeditionMessage, ExpeditionListener> {
/**
* Remembers, per boat ID, the milliseconds difference between the time the message was received
* and the GPS time stamp provided by the message.
*/
private final Map<Integer, Long> timeStampOfLastMessageReceived;
private final ExpeditionMessageParser parser;
/**
* Launches a listener and dumps messages received to the console
@@ -66,128 +46,17 @@ public class UDPExpeditionReceiver implements Runnable {
* start this object in a new thread.
*/
public UDPExpeditionReceiver(int listeningOnPort) throws SocketException {
udpSocket = new DatagramSocket(listeningOnPort);
listeners = new HashMap<ExpeditionListener, Boolean>();
this.listeningOnPort = listeningOnPort;
super(listeningOnPort);
this.timeStampOfLastMessageReceived = new HashMap<Integer, Long>();
}
public void stop() throws SocketException, IOException {
stopped = true;
byte[] buf = new byte[0];
DatagramPacket stopPacket = new DatagramPacket(buf, 0, InetAddress.getLocalHost(), listeningOnPort);
new DatagramSocket().send(stopPacket);
if (!udpSocket.isConnected()) {
udpSocket.close();
}
parser = new ExpeditionMessageParser(this);
}
/**
* If there are currently no listeners subscribed (see {@link #addListener(ExpeditionListener, boolean)} and
* {@link #removeListener(ExpeditionListener)}), this receiver is {@link #stop() stopped} and <code>true</code>
* is returned; otherwise, <code>false</code> is returned.
*/
public synchronized boolean stopIfNoListeners() throws SocketException, IOException {
boolean result = listeners.isEmpty();
if (result) {
stop();
}
return result;
public Map<Integer, Long> getTimeStampOfLastMessageReceived() {
return timeStampOfLastMessageReceived;
}
public boolean isStopped() {
return stopped;
}
public void run() {
byte[] buf = new byte[MAX_PACKET_SIZE];
DatagramPacket p = new DatagramPacket(buf, buf.length);
while (!stopped) {
try {
udpSocket.receive(p);
String packetAsString = new String(p.getData(), p.getOffset(), p.getLength()).trim();
if (packetAsString.length() > 0) {
ExpeditionMessage msg = parse(packetAsString);
if (msg != null) {
for (ExpeditionListener listener : listeners.keySet()) {
if (!listeners.get(listener) || msg.isValid()) {
listener.received(msg);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
udpSocket.close();
if (udpSocket.isConnected()) {
udpSocket.disconnect();
}
}
private ExpeditionMessage parse(String packetAsString) {
Pattern completeLinePattern = Pattern
.compile("#([0-9]*)(( *,([0-9][0-9]*) *, *(-?[0-9]*(\\.[0-9]*)?))*)\\*([0-9a-fA-F][0-9a-fA-F]*)");
Matcher m = completeLinePattern.matcher(packetAsString);
boolean valid = m.matches();
if (valid) {
int boatID = Integer.valueOf(m.group(1));
String variableValuePairs = m.group(2).trim().substring(",".length()); // skip the leading ","
Map<Integer, Double> values = new HashMap<Integer, Double>();
String[] variablesAndValuesInterleaved = variableValuePairs.split(",");
long now = System.currentTimeMillis();
Long diff = timeStampOfLastMessageReceived.get(boatID);
TimePoint defaultForMessageTimePoint;
if (diff != null) {
// compute a reasonable default for a time stamp in case message doesn't provide one
// by subtracting the last diff from now
defaultForMessageTimePoint = new MillisecondsTimePoint(now - diff);
} else {
defaultForMessageTimePoint = null;
}
for (int i = 0; i < variablesAndValuesInterleaved.length; i++) {
int variableID = Integer.valueOf(variablesAndValuesInterleaved[i++]);
double variableValue = Double.valueOf(variablesAndValuesInterleaved[i]);
values.put(variableID, variableValue);
}
int checksum = Integer.valueOf(m.group(m.groupCount()), 16);
valid = valid && checksumOk(checksum, packetAsString);
ExpeditionMessageImpl result;
if (defaultForMessageTimePoint == null) {
result = new ExpeditionMessageImpl(boatID, values, valid);
} else {
result = new ExpeditionMessageImpl(boatID, values, valid, defaultForMessageTimePoint);
}
if (result.hasValue(ExpeditionMessage.ID_GPS_TIME)) {
// an original GPS time stamp; then remember the difference between now and the time stamp
timeStampOfLastMessageReceived.put(boatID, now-result.getTimePoint().asMillis());
}
return result;
} else {
System.err.println("Unparsable expedition message: " + packetAsString);
return null; // couldn't even parse
}
}
private boolean checksumOk(int checksum, String packetAsString) {
int b = 0;
for (byte stringByte : packetAsString.substring(0, packetAsString.lastIndexOf('*')).getBytes()) {
b ^= stringByte;
}
return b == checksum;
}
public synchronized void addListener(ExpeditionListener listener, boolean validMessagesOnly) {
listeners.put(listener, validMessagesOnly);
}
public synchronized void removeListener(ExpeditionListener listener) {
listeners.remove(listener);
}
public int getPort() {
return listeningOnPort;
protected ExpeditionMessageParser getParser() {
return parser;
}
}
@@ -0,0 +1,81 @@
package com.sap.sailing.expeditionconnector.impl;
import java.net.DatagramPacket;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.sap.sailing.domain.base.TimePoint;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.expeditionconnector.ExpeditionMessage;
import com.sap.sailing.expeditionconnector.UDPExpeditionReceiver;
import com.sap.sailing.udpconnector.UDPMessageParser;
public class ExpeditionMessageParser implements UDPMessageParser<ExpeditionMessage> {
private UDPExpeditionReceiver receiver;
public ExpeditionMessageParser(UDPExpeditionReceiver receiver) {
this.receiver = receiver;
}
@Override
public ExpeditionMessage parse(DatagramPacket p) {
String packetAsString = new String(p.getData(), p.getOffset(), p.getLength()).trim();
if (packetAsString.length() > 0) {
Pattern completeLinePattern = Pattern
.compile("#([0-9]*)(( *,([0-9][0-9]*) *, *(-?[0-9]*(\\.[0-9]*)?))*)\\*([0-9a-fA-F][0-9a-fA-F]*)");
Matcher m = completeLinePattern.matcher(packetAsString);
boolean valid = m.matches();
if (valid) {
int boatID = Integer.valueOf(m.group(1));
String variableValuePairs = m.group(2).trim().substring(",".length()); // skip the leading ","
Map<Integer, Double> values = new HashMap<Integer, Double>();
String[] variablesAndValuesInterleaved = variableValuePairs.split(",");
long now = System.currentTimeMillis();
Long diff = receiver.getTimeStampOfLastMessageReceived().get(boatID);
TimePoint defaultForMessageTimePoint;
if (diff != null) {
// compute a reasonable default for a time stamp in case message doesn't provide one
// by subtracting the last diff from now
defaultForMessageTimePoint = new MillisecondsTimePoint(now - diff);
} else {
defaultForMessageTimePoint = null;
}
for (int i = 0; i < variablesAndValuesInterleaved.length; i++) {
int variableID = Integer.valueOf(variablesAndValuesInterleaved[i++]);
double variableValue = Double.valueOf(variablesAndValuesInterleaved[i]);
values.put(variableID, variableValue);
}
int checksum = Integer.valueOf(m.group(m.groupCount()), 16);
valid = valid && checksumOk(checksum, packetAsString);
ExpeditionMessageImpl result;
if (defaultForMessageTimePoint == null) {
result = new ExpeditionMessageImpl(boatID, values, valid);
} else {
result = new ExpeditionMessageImpl(boatID, values, valid, defaultForMessageTimePoint);
}
if (result.hasValue(ExpeditionMessage.ID_GPS_TIME)) {
// an original GPS time stamp; then remember the difference between now and the time stamp
receiver.getTimeStampOfLastMessageReceived().put(boatID, now - result.getTimePoint().asMillis());
}
return result;
} else {
System.err.println("Unparsable expedition message: " + packetAsString);
return null; // couldn't even parse
}
} else {
return null; // empty package
}
}
private boolean checksumOk(int checksum, String packetAsString) {
int b = 0;
for (byte stringByte : packetAsString.substring(0, packetAsString.lastIndexOf('*')).getBytes()) {
b ^= stringByte;
}
return b == checksum;
}
}
@@ -12,7 +12,7 @@
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${workspace_loc:/com.sap.sailing.gwt.ui}\war&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.Leaderboard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war C:\data\SAP\sailing\workspace\java\com.sap.sailing.gwt.ui\war -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.Leaderboard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx512m"/>
</launchConfiguration>
@@ -0,0 +1,29 @@
package com.sap.sailing.gwt.ui.client;
import com.sap.sailing.gwt.ui.shared.LeaderboardDAO;
/**
* An editable version of the {@link LeaderboardPanel} which allows a user to enter carried / accumulated
* points and fix individual race scores.
*
* @author Axel Uhl (d043530)
*
*/
public class EditableLeaderboardPanel extends LeaderboardPanel {
public EditableLeaderboardPanel(SailingServiceAsync sailingService, String leaderboardName,
ErrorReporter errorReporter, StringConstants stringConstants) {
super(sailingService, leaderboardName, errorReporter, stringConstants);
}
/**
* Always ensures that there is a carry column displayed because in the editable version
* of the leaderboard the carried / accumulated values must always be editable and therefore
* the column must always be shown.
*/
@Override
protected void updateCarryColumn(LeaderboardDAO leaderboard) {
ensureCarryColumn();
}
}
@@ -162,7 +162,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer,
grid.setWidget(3, 0, selectedLeaderboardPanel);
columnNamesInSelectedLeaderboardListBox = new ListBox();
selectedLeaderboardPanel.add(columnNamesInSelectedLeaderboardListBox);
columnNamesInSelectedLeaderboardListBox.setVisibleItemCount(5);
columnNamesInSelectedLeaderboardListBox.setVisibleItemCount(10);
columnNamesInSelectedLeaderboardListBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent e) {
@@ -47,7 +47,7 @@ public class LeaderboardPanel extends FormPanel {
private final StringConstants stringConstants;
private CellTable<LeaderboardRowDAO> leaderboardTable;
private final CellTable<LeaderboardRowDAO> leaderboardTable;
private ListDataProvider<LeaderboardRowDAO> data;
@@ -196,7 +196,7 @@ public class LeaderboardPanel extends FormPanel {
}
}
private class CarryColumn extends SortableColumn<LeaderboardRowDAO> {
protected class CarryColumn extends SortableColumn<LeaderboardRowDAO> {
public CarryColumn() {
setSortable(true);
}
@@ -229,12 +229,12 @@ public class LeaderboardPanel extends FormPanel {
this.errorReporter = errorReporter;
this.stringConstants = stringConstants;
leaderboardTable = new CellTable<LeaderboardRowDAO>(/* pageSize */ 100);
leaderboardTable.setWidth("100%");
leaderboardTable.setSelectionModel(new MultiSelectionModel<LeaderboardRowDAO>() {});
getLeaderboardTable().setWidth("100%");
getLeaderboardTable().setSelectionModel(new MultiSelectionModel<LeaderboardRowDAO>() {});
data = new ListDataProvider<LeaderboardRowDAO>();
data.addDataDisplay(leaderboardTable);
data.addDataDisplay(getLeaderboardTable());
listHandler = new ListHandler<LeaderboardRowDAO>(data.getList());
leaderboardTable.addColumnSortHandler(listHandler);
getLeaderboardTable().addColumnSortHandler(listHandler);
loadCompleteLeaderboard(new Date());
VerticalPanel vp = new VerticalPanel();
HorizontalPanel hp = new HorizontalPanel();
@@ -249,12 +249,12 @@ public class LeaderboardPanel extends FormPanel {
}
});
vp.add(hp);
vp.add(leaderboardTable);
vp.add(getLeaderboardTable());
setWidget(vp);
}
private void addColumn(SortableColumn<LeaderboardRowDAO> column) {
leaderboardTable.addColumn(column, column.getHeader());
protected void addColumn(SortableColumn<LeaderboardRowDAO> column) {
getLeaderboardTable().addColumn(column, column.getHeader());
listHandler.setComparator(column, column.getComparator());
}
@@ -294,8 +294,8 @@ public class LeaderboardPanel extends FormPanel {
private void createMissingRaceColumns(LeaderboardDAO leaderboard) {
for (Map.Entry<String, Boolean> raceNameAndMedalRace : leaderboard.raceNamesAndMedalRace.entrySet()) {
boolean foundRaceColumn = false;
for (int i=0; !foundRaceColumn && i<leaderboardTable.getColumnCount(); i++) {
Column<LeaderboardRowDAO, ?> c = leaderboardTable.getColumn(i);
for (int i=0; !foundRaceColumn && i<getLeaderboardTable().getColumnCount(); i++) {
Column<LeaderboardRowDAO, ?> c = getLeaderboardTable().getColumn(i);
if (c instanceof RaceColumn && ((RaceColumn) c).getRaceName().equals(raceNameAndMedalRace.getKey())) {
foundRaceColumn = true;
}
@@ -308,14 +308,14 @@ public class LeaderboardPanel extends FormPanel {
private void removeUnusedRaceColumns(LeaderboardDAO leaderboard) {
List<Column<LeaderboardRowDAO, ?>> columnsToRemove = new ArrayList<Column<LeaderboardRowDAO,?>>();
for (int i=0; i<leaderboardTable.getColumnCount(); i++) {
Column<LeaderboardRowDAO, ?> c = leaderboardTable.getColumn(i);
for (int i=0; i<getLeaderboardTable().getColumnCount(); i++) {
Column<LeaderboardRowDAO, ?> c = getLeaderboardTable().getColumn(i);
if (c instanceof RaceColumn && (leaderboard == null || !leaderboard.raceNamesAndMedalRace.keySet().contains(((RaceColumn) c).getRaceName()))) {
columnsToRemove.add(c);
}
}
for (Column<LeaderboardRowDAO, ?> c : columnsToRemove) {
leaderboardTable.removeColumn(c);
getLeaderboardTable().removeColumn(c);
}
}
@@ -323,47 +323,61 @@ public class LeaderboardPanel extends FormPanel {
* If the last column is the totals column, remove it. Add the race column as the last column.
*/
private void addRaceColumn(RaceColumn raceColumn) {
if (leaderboardTable.getColumn(leaderboardTable.getColumnCount()-1) instanceof TotalsColumn) {
leaderboardTable.removeColumn(leaderboardTable.getColumnCount()-1);
if (getLeaderboardTable().getColumn(getLeaderboardTable().getColumnCount()-1) instanceof TotalsColumn) {
getLeaderboardTable().removeColumn(getLeaderboardTable().getColumnCount()-1);
}
addColumn(raceColumn);
}
private void ensureCompetitorColumn() {
if (leaderboardTable.getColumnCount() == 0) {
if (getLeaderboardTable().getColumnCount() == 0) {
addColumn(new CompetitorColumn());
} else {
if (!(leaderboardTable.getColumn(0) instanceof CompetitorColumn)) {
if (!(getLeaderboardTable().getColumn(0) instanceof CompetitorColumn)) {
throw new RuntimeException("The first column must always be the competitor column but it was of type "+
leaderboardTable.getColumn(0).getClass().getName());
getLeaderboardTable().getColumn(0).getClass().getName());
}
}
}
private void ensureTotalsColumn() {
// add a totals column on the right
if (leaderboardTable.getColumnCount() == 0 || !(leaderboardTable.getColumn(leaderboardTable.getColumnCount()-1) instanceof TotalsColumn)) {
if (getLeaderboardTable().getColumnCount() == 0 || !(getLeaderboardTable().getColumn(getLeaderboardTable().getColumnCount()-1) instanceof TotalsColumn)) {
addColumn(new TotalsColumn());
}
}
/**
* If column #1 (second column, right of the competitor column) does not exist or is not of type {@link CarryColumn},
* all columns starting from #1 will be removed and a {@link CarryColumn} will be added.
* If the <code>leaderboard</code> {@link LeaderboardDAO#hasCarriedPoints has carried points} and if column #1
* (second column, right of the competitor column) does not exist or is not of type {@link CarryColumn}, all columns
* starting from #1 will be removed and a {@link CarryColumn} will be added. If the leaderboard has no carried points
* but the display still shows a carry column, the column is removed.
*/
private void updateCarryColumn(LeaderboardDAO leaderboard) {
protected void updateCarryColumn(LeaderboardDAO leaderboard) {
if (leaderboard != null && leaderboard.hasCarriedPoints) {
if (!(leaderboardTable.getColumn(1) instanceof CarryColumn)) {
while (leaderboardTable.getColumnCount() > 1) {
leaderboardTable.removeColumn(1);
}
addColumn(new CarryColumn());
}
ensureCarryColumn();
} else {
if (leaderboardTable.getColumnCount() >= 2 && leaderboardTable.getColumn(1) instanceof CarryColumn) {
leaderboardTable.removeColumn(1);
}
ensureNoCarryColumn();
}
}
private void ensureNoCarryColumn() {
if (getLeaderboardTable().getColumnCount() >= 2 && getLeaderboardTable().getColumn(1) instanceof CarryColumn) {
getLeaderboardTable().removeColumn(1);
}
}
protected void ensureCarryColumn() {
if (!(getLeaderboardTable().getColumn(1) instanceof CarryColumn)) {
while (getLeaderboardTable().getColumnCount() > 1) {
getLeaderboardTable().removeColumn(1);
}
addColumn(new CarryColumn());
}
}
protected CellTable<LeaderboardRowDAO> getLeaderboardTable() {
return leaderboardTable;
}
}
@@ -1,2 +0,0 @@
/com.sap.sailing.gwt.ui.AdminConsole
/com.sap.sailing.gwt.ui.Leaderboard
@@ -1,2 +0,0 @@
# Module com.sap.sailing.gwt.ui.Leaderboard
# RPC service class, partial path of RPC policy file
@@ -24,5 +24,5 @@
<booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/>
<booleanAttribute key="useDefaultConfigArea" value="false"/>
<stringAttribute key="workspace_bundles" value="com.google.gwt.osgi@default:default,com.googlecode.java-diff-utils.test@default:default,com.googlecode.java-diff-utils@default:default,com.mongodb.driver@default:default,com.sap.sailing.declination.test@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.swisstimingadapter.test@default:default,com.sap.sailing.domain.swisstimingadapter@default:default,com.sap.sailing.domain.test@default:default,com.sap.sailing.domain.tractracadapter@default:default,com.sap.sailing.domain@default:default,com.sap.sailing.expeditionconnector.test@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.gwt.ui@default:default,com.sap.sailing.mongodb.test@default:default,com.sap.sailing.mongodb@default:default,com.sap.sailing.server.test@default:default,com.sap.sailing.server@default:default,com.sap.ui.phoenix.resource-osgi@default:false,com.sap.ui.phoenix.resource-servlet@default:default,com.tractrac.clientmodule@default:default,com.tractrac.resultapi@default:default,org.json.simple@default:default"/>
<stringAttribute key="workspace_bundles" value="com.google.gwt.osgi@default:default,com.googlecode.java-diff-utils.test@default:default,com.googlecode.java-diff-utils@default:default,com.mongodb.driver@default:default,com.sap.sailing.declination.test@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.test@default:default,com.sap.sailing.domain.tractracadapter@default:default,com.sap.sailing.domain@default:default,com.sap.sailing.expeditionconnector.test@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.gwt.ui@default:default,com.sap.sailing.mongodb.test@default:default,com.sap.sailing.mongodb@default:default,com.sap.sailing.server.test@default:default,com.sap.sailing.server@default:default,com.sap.ui.phoenix.resource-osgi@default:false,com.sap.ui.phoenix.resource-servlet@default:default,com.tractrac.clientmodule@default:default,com.tractrac.resultapi@default:default,org.json.simple@default:default"/>
</launchConfiguration>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.udpconnector</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,8 @@
#Mon Aug 29 13:20:38 CEST 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6
@@ -0,0 +1,4 @@
#Mon Aug 29 13:20:38 CEST 2011
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
+8
View File
@@ -0,0 +1,8 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Udpconnector
Bundle-SymbolicName: com.sap.sailing.udpconnector
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Export-Package: com.sap.sailing.udpconnector
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>root</artifactId>
<groupId>com.sap.sailing</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>com.sap.sailing.udpconnector</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
</project>
@@ -0,0 +1,5 @@
package com.sap.sailing.udpconnector;
public interface UDPMessage {
boolean isValid();
}
@@ -0,0 +1,5 @@
package com.sap.sailing.udpconnector;
public interface UDPMessageListener<MessageType extends UDPMessage> {
void received(MessageType message);
}
@@ -0,0 +1,7 @@
package com.sap.sailing.udpconnector;
import java.net.DatagramPacket;
public interface UDPMessageParser<MessageType> {
MessageType parse(DatagramPacket p);
}
@@ -0,0 +1,105 @@
package com.sap.sailing.udpconnector;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
public abstract class UDPReceiver<MessageType extends UDPMessage, ListenerType extends UDPMessageListener<MessageType>> implements Runnable {
private boolean stopped = false;
private final int listeningOnPort;
private final DatagramSocket udpSocket;
/**
* For each listener remembers if the listener is only interested in valid messages.
*/
private final Map<ListenerType, Boolean> listeners;
/**
* Maximum IP packet length
*/
private static final int MAX_PACKET_SIZE = 65536;
/**
* You need call {@link #run} to actually start receiving events. To do this asynchronously,
* start this object in a new thread.
*/
public UDPReceiver(int listeningOnPort) throws SocketException {
udpSocket = new DatagramSocket(listeningOnPort);
listeners = new HashMap<ListenerType, Boolean>();
this.listeningOnPort = listeningOnPort;
}
public void stop() throws SocketException, IOException {
stopped = true;
byte[] buf = new byte[0];
DatagramPacket stopPacket = new DatagramPacket(buf, 0, InetAddress.getLocalHost(), listeningOnPort);
new DatagramSocket().send(stopPacket);
if (!udpSocket.isConnected()) {
udpSocket.close();
}
}
/**
* If there are currently no listeners subscribed (see {@link #addListener(ListenerType, boolean)} and
* {@link #removeListener(ListenerType)}), this receiver is {@link #stop() stopped} and <code>true</code>
* is returned; otherwise, <code>false</code> is returned.
*/
public synchronized boolean stopIfNoListeners() throws SocketException, IOException {
boolean result = listeners.isEmpty();
if (result) {
stop();
}
return result;
}
public boolean isStopped() {
return stopped;
}
public void run() {
byte[] buf = new byte[MAX_PACKET_SIZE];
DatagramPacket p = new DatagramPacket(buf, buf.length);
while (!stopped) {
try {
udpSocket.receive(p);
String packetAsString = new String(p.getData(), p.getOffset(), p.getLength()).trim();
if (packetAsString.length() > 0) {
MessageType msg = getParser().parse(p);
if (msg != null) {
for (ListenerType listener : listeners.keySet()) {
if (!listeners.get(listener) || msg.isValid()) {
listener.received(msg);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
udpSocket.close();
if (udpSocket.isConnected()) {
udpSocket.disconnect();
}
}
public synchronized void addListener(ListenerType listener, boolean validMessagesOnly) {
listeners.put(listener, validMessagesOnly);
}
public synchronized void removeListener(ListenerType listener) {
listeners.remove(listener);
}
public int getPort() {
return listeningOnPort;
}
protected abstract UDPMessageParser<MessageType> getParser();
}
+1
View File
@@ -21,6 +21,7 @@
<module>com.sap.sailing.domain.swisstimingadapter.test</module>
<module>com.sap.sailing.declination</module>
<module>com.sap.sailing.declination.test</module>
<module>com.sap.sailing.udpconnector</module>
<module>com.sap.sailing.expeditionconnector</module>
<module>com.sap.sailing.expeditionconnector.test</module>
<module>com.sap.sailing.server</module>