mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-05 11:53:53 +00:00
factored commonalitied between Expedition and SwissTiming UDP receivers
This commit is contained in:
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
-1
@@ -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;
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.sap.sailing.domain.swisstimingadapter;
|
||||
|
||||
public interface SwissTimingListener {
|
||||
void received(SwissTimingMessage message);
|
||||
}
|
||||
+2
-1
@@ -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();
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package com.sap.sailing.domain.swisstimingadapter;
|
||||
|
||||
import com.sap.sailing.udpconnector.UDPMessageListener;
|
||||
|
||||
public interface SwissTimingMessageListener extends UDPMessageListener<SwissTimingMessage> {
|
||||
}
|
||||
+2
-1
@@ -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;
|
||||
|
||||
+5
@@ -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() {
|
||||
|
||||
+7
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -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>
|
||||
-18
@@ -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());
|
||||
}
|
||||
}
|
||||
+14
@@ -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());
|
||||
}
|
||||
}
|
||||
-16
@@ -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.
|
||||
|
||||
+19
-1
@@ -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
|
||||
+3
-2
@@ -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> {
|
||||
}
|
||||
+2
-1
@@ -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
|
||||
*/
|
||||
|
||||
+11
-142
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+81
@@ -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 "${workspace_loc:/com.sap.sailing.gwt.ui}\war" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -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 "${gwt_remote_ui_server_port}:${unique_id}" -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>
|
||||
+29
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+46
-32
@@ -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
|
||||
-2
@@ -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>
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
Executable
+28
@@ -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
|
||||
@@ -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
|
||||
@@ -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>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.sap.sailing.udpconnector;
|
||||
|
||||
public interface UDPMessage {
|
||||
boolean isValid();
|
||||
}
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
package com.sap.sailing.udpconnector;
|
||||
|
||||
public interface UDPMessageListener<MessageType extends UDPMessage> {
|
||||
void received(MessageType message);
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.sap.sailing.udpconnector;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
|
||||
public interface UDPMessageParser<MessageType> {
|
||||
MessageType parse(DatagramPacket p);
|
||||
}
|
||||
+105
@@ -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();
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user