diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java index a0defac30e2..d34a6f12612 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java @@ -69,7 +69,7 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest { domainEvent = domainFactory.createEvent(getEvent()); trackedEvent = domainFactory.getOrCreateTrackedEvent(domainEvent); receivers.add(new RaceCourseReceiver(domainFactory, trackedEvent, getEvent(), /* millisecondsOverWhichToAverageWind */ - EmptyWindStore.INSTANCE, + EmptyWindStore.INSTANCE, this, 30000, /* millisecondsOverWhichToAverageSpeed */30000) { @Override protected void handleEvent(Triple event) { diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java index 4689bde19e8..56d7591dfc6 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java @@ -90,12 +90,12 @@ public class ReceiveTrackingDataTest extends AbstractTracTracLiveTest { public void raceRemoved(TrackedRace trackedRace) { } }); - for (Receiver receiver : domainFactory.getUpdateReceivers(trackedEvent, getEvent(), EmptyWindStore.INSTANCE)) { + for (Receiver receiver : domainFactory.getUpdateReceivers(trackedEvent, getEvent(), EmptyWindStore.INSTANCE, this)) { for (TypeController raceListener : receiver.getTypeControllers()) { listeners.add(raceListener); } } - addListenersForStoredDataAndStartController(domainFactory.getUpdateReceivers(trackedEvent, getEvent(), EmptyWindStore.INSTANCE)); + addListenersForStoredDataAndStartController(domainFactory.getUpdateReceivers(trackedEvent, getEvent(), EmptyWindStore.INSTANCE, this)); } @Test diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java index f4e232b2603..a36f1f78c50 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java @@ -114,23 +114,22 @@ public interface DomainFactory { BoatClass getBoatClass(CompetitorClass competitorClass); /** - * For each race listed by the tractracEvent, produces - * {@link TypeController listeners} that, when - * {@link DataController#add(TypeController) registered} with the - * controller, create a {@link RaceDefinition} with the {@link Course} - * defined in proper order, and - * {@link com.sap.sailing.domain.base.Event#addRace(RaceDefinition) add} it - * to the event. Other listeners of those returned will listen - * for raw position and aggregated position data and update the - * {@link TrackedEvent}'s content accordingly. + * For each race listed by the tractracEvent, produces {@link TypeController listeners} that, when + * {@link DataController#add(TypeController) registered} with the controller, create a {@link RaceDefinition} with + * the {@link Course} defined in proper order, and {@link com.sap.sailing.domain.base.Event#addRace(RaceDefinition) + * add} it to the event. Other listeners of those returned will listen for raw position and aggregated + * position data and update the {@link TrackedEvent}'s content accordingly. * * @param trackedEvent * must have been created before through - * {@link #getOrCreateTrackedEvent(com.sap.sailing.domain.base.Event)} because - * otherwise the link to the {@link Event} can't be established - * @param windStore TODO + * {@link #getOrCreateTrackedEvent(com.sap.sailing.domain.base.Event)} because otherwise the link to the + * {@link Event} can't be established + * @param tokenToRetrieveAssociatedRace + * used in {@link #getRace} to retrieve the {@link RaceDefinition} received by the + * {@link RaceCourseReceiver} created by this call */ - Iterable getUpdateReceivers(DynamicTrackedEvent trackedEvent, Event tractracEvent, WindStore windStore); + Iterable getUpdateReceivers(DynamicTrackedEvent trackedEvent, Event tractracEvent, WindStore windStore, + Object tokenToRetrieveAssociatedRace); RaceDefinition createRaceDefinition(Race race, Course course); @@ -145,17 +144,19 @@ public interface DomainFactory { MarkPassing createMarkPassing(com.tractrac.clientmodule.Competitor competitor, Waypoint passed, TimePoint time); - Iterable getUpdateReceivers(DynamicTrackedEvent trackedEvent, Event tractracEvent, WindStore windStore, ReceiverType... types); + Iterable getUpdateReceivers(DynamicTrackedEvent trackedEvent, Event tractracEvent, WindStore windStore, + Object tokenToRetrieveAssociatedRace, ReceiverType... types); - DynamicTrackedRace trackRace(TrackedEvent trackedEvent, RaceDefinition raceDefinition, - WindStore windStore, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, Event tractracEvent); + DynamicTrackedRace trackRace(TrackedEvent trackedEvent, RaceDefinition raceDefinition, WindStore windStore, + long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, Event tractracEvent, + Object tokenToRetrieveAssociatedRace); /** - * Non-blocking call that returns null if the {@link RaceDefinition} for the TracTrac Event + * Non-blocking call that returns null if the {@link RaceDefinition} for the token * hasn't been created yet, e.g., because the course definition hasn't been received yet or the listener * for receiving course information hasn't been registered (yet). */ - RaceDefinition getRace(Event tractracEvent); + RaceDefinition getRace(Object tokenToRetrieveAssociatedRace); JSONService parseJSONURL(URL jsonURL) throws IOException, ParseException, org.json.simple.parser.ParseException; diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java index 23518380c88..e21b8a130eb 100644 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java @@ -105,7 +105,12 @@ public class DomainFactoryImpl implements DomainFactory { private final Map, com.sap.sailing.domain.base.Event> eventCache = new HashMap, com.sap.sailing.domain.base.Event>(); - private final Map tractracEventToRaceDefinitionMap = new HashMap(); + /** + * Keyed by an object that the client that started the {@link RaceCourseReceiver} which then provided the + * {@link RaceDefinition} has provided to retrieve the {@link RaceDefinition} again using + * {@link #getRace}. + */ + private final Map tractracEventToRaceDefinitionMap = new HashMap(); private final Map raceCache = new HashMap(); @@ -321,13 +326,15 @@ public class DomainFactoryImpl implements DomainFactory { } @Override - public Iterable getUpdateReceivers(DynamicTrackedEvent trackedEvent, com.tractrac.clientmodule.Event tractracEvent, WindStore windStore, ReceiverType... types) { + public Iterable getUpdateReceivers(DynamicTrackedEvent trackedEvent, com.tractrac.clientmodule.Event tractracEvent, + WindStore windStore, Object tokenToRetrieveAssociatedRace, ReceiverType... types) { Collection result = new ArrayList(); for (ReceiverType type : types) { switch (type) { case RACECOURSE: result.add(new RaceCourseReceiver( - this, trackedEvent, tractracEvent, windStore, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed)); + this, trackedEvent, tractracEvent, windStore, + tokenToRetrieveAssociatedRace, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed)); break; case MARKPOSITIONS: result.add(new MarkPositionReceiver( @@ -352,8 +359,8 @@ public class DomainFactoryImpl implements DomainFactory { @Override public Iterable getUpdateReceivers(DynamicTrackedEvent trackedEvent, - com.tractrac.clientmodule.Event tractracEvent, WindStore windStore) { - return getUpdateReceivers(trackedEvent, tractracEvent, windStore, ReceiverType.RACECOURSE, + com.tractrac.clientmodule.Event tractracEvent, WindStore windStore, Object tokenToRetrieveAssociatedRace) { + return getUpdateReceivers(trackedEvent, tractracEvent, windStore, tokenToRetrieveAssociatedRace, ReceiverType.RACECOURSE, ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS); } @@ -445,18 +452,24 @@ public class DomainFactoryImpl implements DomainFactory { } @Override - public RaceDefinition getRace(com.tractrac.clientmodule.Event tractracEvent) { - return tractracEventToRaceDefinitionMap.get(tractracEvent); + public RaceDefinition getRace(Object tokenToRetrieveAssociatedRace) { + return tractracEventToRaceDefinitionMap.get(tokenToRetrieveAssociatedRace); } @Override public DynamicTrackedRace trackRace(TrackedEvent trackedEvent, RaceDefinition raceDefinition, WindStore windStore, long millisecondsOverWhichToAverageWind, - long millisecondsOverWhichToAverageSpeed, com.tractrac.clientmodule.Event tractracEvent) { + long millisecondsOverWhichToAverageSpeed, com.tractrac.clientmodule.Event tractracEvent, Object tokenToRetrieveAssociatedRace) { logger.log(Level.INFO, "Creating DynamicTrackedRaceImpl for RaceDefinition "+raceDefinition.getName()); DynamicTrackedRaceImpl result = new DynamicTrackedRaceImpl(trackedEvent, raceDefinition, windStore, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed); - tractracEventToRaceDefinitionMap.put(tractracEvent, raceDefinition); + if (!tractracEventToRaceDefinitionMap.containsKey(tokenToRetrieveAssociatedRace)) { + tractracEventToRaceDefinitionMap.put(tokenToRetrieveAssociatedRace, raceDefinition); + } else { + logger.severe("There is already something ("+tokenToRetrieveAssociatedRace+") tracking "+ + tractracEventToRaceDefinitionMap.get(tokenToRetrieveAssociatedRace)+" while trying to start tracking for race "+ + raceDefinition); + } return result; } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java index fc15b9e23fa..87766983745 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java @@ -40,9 +40,11 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue(); Set typeControllers = new HashSet(); - for (Receiver receiver : domainFactory.getUpdateReceivers(trackedEvent, tractracEvent, windStore)) { + for (Receiver receiver : domainFactory.getUpdateReceivers(trackedEvent, tractracEvent, windStore, this)) { receivers.add(receiver); for (TypeController typeController : receiver.getTypeControllers()) { typeControllers.add(typeController); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracks.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracks.java index 3dc22159d0e..516e4b67ee0 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracks.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracks.java @@ -69,7 +69,7 @@ public class TestStoringAndRetrievingWindTracks extends AbstractTracTracLiveTest RaceDefinition race = domainFactory.getRaceDefinition(getEvent().getRaceList().iterator().next()); DynamicTrackedRace trackedRace = domainFactory.trackRace(trackedEvent, race, /* millisecondsOverWhichToAverageWind */ EmptyWindStore.INSTANCE, /* millisecondsOverWhichToAverageSpeed */ - 30000, 10000, getEvent()); + 30000, 10000, getEvent(), this); WindSource windSource = WindSource.WEB; MongoObjectFactory.INSTANCE.addWindTrackDumper(trackedEvent, trackedRace, windSource, db); WindTrack windTrack = trackedRace.getWindTrack(windSource); diff --git a/python/src/sailing.connector/sailing/connector/provider.py b/python/src/sailing.connector/sailing/connector/provider.py index d70bcc63ed8..0ea8fe7f4c4 100644 --- a/python/src/sailing.connector/sailing/connector/provider.py +++ b/python/src/sailing.connector/sailing/connector/provider.py @@ -43,7 +43,9 @@ class LiveDataReceiver(threading.Thread): # make it possible to pause listener if self.paused is True: - time.sleep(1) + print >>sys.stderr, '#', + sys.stderr.flush() + time.sleep(5) continue conf = URIConfigurator(self.host, self.port) @@ -62,7 +64,7 @@ class LiveDataReceiver(threading.Thread): else: conf.setParameters(dict(eventname=self.eventname, racename=self.racename, sinceupdate=self.updatecount)) - conf.setLogging(True) + conf.setLogging(False) try: # need to lock because other threads could overwrite information diff --git a/python/src/sailing.web/sailing/web/browser/core.py b/python/src/sailing.web/sailing/web/browser/core.py index c7a9c92e46e..c52c8098205 100644 --- a/python/src/sailing.web/sailing/web/browser/core.py +++ b/python/src/sailing.web/sailing/web/browser/core.py @@ -231,13 +231,16 @@ class BaseView(object): if name[0] == 'ETASEC': val = '%.f:%d' % (int(value) / 60, int(value)-( (int(value)/60)*60 )) if len(val.split(':')[-1]) == 1: - val = '%s0' % val + val = '0%s' % val + + if val == '00:0': + val = '0:0' if name[0] == 'GLP': if value > 0: - val = '%s' % value + val = '%s' % value elif value < 0: - val = '%s' % abs(value) + val = '%s' % abs(value) else: val = '0' return val diff --git a/python/src/sailing.web/sailing/web/browser/functions.py b/python/src/sailing.web/sailing/web/browser/functions.py index e9d1aae7d56..a2bef900c98 100644 --- a/python/src/sailing.web/sailing/web/browser/functions.py +++ b/python/src/sailing.web/sailing/web/browser/functions.py @@ -180,6 +180,12 @@ def configureListener(context, request): view.session['listener-started'] = True view.session.save() + elif request.POST.get('emergency-reload', None): + state = reloadData(context, request) + if state is True: + return view.yieldMessage('Data refreshed! Can take a while until listener threads recover. Make sure to also reload running Leaderboards!') + return view.yieldMessage('Could not refresh data - this should NEVER happen :-)') + elif request.POST.get('disconnect-server', None): # disconnect does not stop listeners but just disconnects the # session of the user from server @@ -696,21 +702,28 @@ def reloadData(context, request): # drops all data in database and reload all running listeners view = core.BaseView(context, request) - event = view.currentEvent() - if event: + # pause all listener threads + for listener in threaded_listener.values(): + if listener.paused == False: + listener.paused = True - # pause all listener threads - for listener in threaded_listener.values(): - if listener.paused == False: - listener.paused = True + # give threads time to pause + time.sleep(10) - # remove competitors matching the event - model.CompetitorImpl.removeAllBy(event=event.name) + # drop DB with all data in it + from sailing.db.monitoring import dropDB + dropDB() - # reload event data completely - configurator = view.listenerConf() - configurator.setContext(config.MODERATOR) - configurator.setCommand(config.LIST_EVENTS) - provider.eventConfiguration(configurator) + # reload event data completely + configurator = view.listenerConf() + configurator.setContext(config.MODERATOR) + configurator.setCommand(config.LIST_EVENTS) + provider.eventConfiguration(configurator) + # start threads again + for listener in threaded_listener.values(): + if listener.paused == True: + listener.paused = False + + return True diff --git a/python/src/sailing.web/sailing/web/browser/moderator-static/app.js b/python/src/sailing.web/sailing/web/browser/moderator-static/app.js index 19750ad02ac..dea442714c9 100644 --- a/python/src/sailing.web/sailing/web/browser/moderator-static/app.js +++ b/python/src/sailing.web/sailing/web/browser/moderator-static/app.js @@ -108,14 +108,13 @@ function sortBy(param, element) { } function yieldValue(element, newvalue, ignore_zeros, alternate_value) { - if (element.html() != newvalue) { if (ignore_zeros == true && (newvalue == '0' || newvalue == 0)) { // show alternate value if (alternate_value != undefined && alternate_value != '' && alternate_value != 'None') { element.html(alternate_value); } else { - element.html(' '); + element.html('—'); } return; } @@ -132,11 +131,6 @@ function yieldValue(element, newvalue, ignore_zeros, alternate_value) { else element.html(newvalue); - } else { - if (alternate_value != undefined && alternate_value != '' && alternate_value != 'None') { - element.html(alternate_value); - } - } } /** diff --git a/python/src/sailing.web/sailing/web/browser/moderator-static/arrow-gain.png b/python/src/sailing.web/sailing/web/browser/moderator-static/arrow-gain.png index fe9bcff7da3..1d0d57157aa 100644 Binary files a/python/src/sailing.web/sailing/web/browser/moderator-static/arrow-gain.png and b/python/src/sailing.web/sailing/web/browser/moderator-static/arrow-gain.png differ diff --git a/python/src/sailing.web/sailing/web/browser/moderator-static/arrow-loss.png b/python/src/sailing.web/sailing/web/browser/moderator-static/arrow-loss.png index aba1e973718..c76f0deef04 100644 Binary files a/python/src/sailing.web/sailing/web/browser/moderator-static/arrow-loss.png and b/python/src/sailing.web/sailing/web/browser/moderator-static/arrow-loss.png differ diff --git a/python/src/sailing.web/sailing/web/browser/moderator-static/flags/de.png b/python/src/sailing.web/sailing/web/browser/moderator-static/flags/de.png index 689b3b7f82a..7d4908509d9 100644 Binary files a/python/src/sailing.web/sailing/web/browser/moderator-static/flags/de.png and b/python/src/sailing.web/sailing/web/browser/moderator-static/flags/de.png differ diff --git a/python/src/sailing.web/sailing/web/browser/templates/java-connector.pt b/python/src/sailing.web/sailing/web/browser/templates/java-connector.pt index 38fee408e1b..a69fdcc1b9b 100644 --- a/python/src/sailing.web/sailing/web/browser/templates/java-connector.pt +++ b/python/src/sailing.web/sailing/web/browser/templates/java-connector.pt @@ -29,8 +29,9 @@ When stopping listener it will trigger two methods: If you select a race
Handling   -     +     +    
diff --git a/python/src/sailing.web/sailing/web/browser/templates/leaderboard-live.pt b/python/src/sailing.web/sailing/web/browser/templates/leaderboard-live.pt index d3ffcd85d4d..6e301c2b380 100644 --- a/python/src/sailing.web/sailing/web/browser/templates/leaderboard-live.pt +++ b/python/src/sailing.web/sailing/web/browser/templates/leaderboard-live.pt @@ -73,7 +73,7 @@ -
+
@@ -117,7 +117,7 @@ ${racebundles[int(request.params.get('r', 0))].index(bundle)+1+(int(request.params.get('r', 0))*3)} - +
@@ -148,7 +148,7 @@ - + @@ -172,7 +172,7 @@ M${markid-1} F - +