diff --git a/src/main/java/telraam/App.java b/src/main/java/telraam/App.java index 08a3847..4f4348c 100644 --- a/src/main/java/telraam/App.java +++ b/src/main/java/telraam/App.java @@ -1,11 +1,11 @@ package telraam; import io.dropwizard.core.Application; +import io.dropwizard.core.setup.Bootstrap; +import io.dropwizard.core.setup.Environment; import io.dropwizard.jdbi3.JdbiFactory; import io.dropwizard.jdbi3.bundles.JdbiExceptionsBundle; import io.dropwizard.jersey.setup.JerseyEnvironment; -import io.dropwizard.core.setup.Bootstrap; -import io.dropwizard.core.setup.Environment; import io.federecio.dropwizard.swagger.SwaggerBundle; import io.federecio.dropwizard.swagger.SwaggerBundleConfiguration; import jakarta.servlet.DispatcherType; @@ -24,13 +24,11 @@ import telraam.logic.lapper.robust.RobustLapper; import telraam.logic.lapper.slapper.Slapper; import telraam.logic.positioner.Positioner; -import telraam.logic.positioner.simple.SimplePositioner; +import telraam.logic.positioner.nostradamus.Nostradamus; import telraam.station.FetcherFactory; -import telraam.station.websocket.WebsocketFetcher; import telraam.util.AcceptedLapsUtil; import telraam.websocket.WebSocketConnection; -import java.io.IOException; import java.util.EnumSet; import java.util.HashSet; import java.util.Set; @@ -92,11 +90,11 @@ public void run(AppConfiguration configuration, Environment environment) { // Register websocket endpoint JettyWebSocketServletContainerInitializer.configure( - environment.getApplicationContext(), - (servletContext, wsContainer) -> { - wsContainer.setMaxTextMessageSize(65535); - wsContainer.addMapping("/ws", (req, res) -> new WebSocketConnection()); - } + environment.getApplicationContext(), + (servletContext, wsContainer) -> { + wsContainer.setMaxTextMessageSize(65535); + wsContainer.addMapping("/ws", (req, res) -> new WebSocketConnection()); + } ); // Add api resources @@ -142,7 +140,7 @@ public void run(AppConfiguration configuration, Environment environment) { // Set up positioners Set positioners = new HashSet<>(); - positioners.add(new SimplePositioner(this.database)); + positioners.add(new Nostradamus(this.database)); // Start fetch thread for each station FetcherFactory fetcherFactory = new FetcherFactory(this.database, lappers, positioners); diff --git a/src/main/java/telraam/api/TeamResource.java b/src/main/java/telraam/api/TeamResource.java index 5983eff..9bf36c3 100644 --- a/src/main/java/telraam/api/TeamResource.java +++ b/src/main/java/telraam/api/TeamResource.java @@ -51,9 +51,6 @@ public Team update(Team team, Optional id) { Team previousTeam = this.get(id); Team ret = super.update(team, id); - System.out.println(previousTeam.getBatonId()); - System.out.println(team.getBatonId()); - if (!Objects.equals(previousTeam.getBatonId(), team.getBatonId())) { this.batonSwitchoverDAO.insert(new BatonSwitchover( team.getId(), diff --git a/src/main/java/telraam/database/models/Detection.java b/src/main/java/telraam/database/models/Detection.java index 5cd050b..4950feb 100644 --- a/src/main/java/telraam/database/models/Detection.java +++ b/src/main/java/telraam/database/models/Detection.java @@ -31,4 +31,10 @@ public Detection(Integer batonId, Integer stationId, Integer rssi, Float battery this.timestamp = timestamp; this.timestampIngestion = timestampIngestion; } + + public Detection(Integer id, Integer stationId, Integer rssi) { + this.id = id; + this.stationId = stationId; + this.rssi = rssi; + } } diff --git a/src/main/java/telraam/database/models/Station.java b/src/main/java/telraam/database/models/Station.java index 442e412..b1f1220 100644 --- a/src/main/java/telraam/database/models/Station.java +++ b/src/main/java/telraam/database/models/Station.java @@ -35,4 +35,8 @@ public Station(String name, boolean isBroken) { this.name = name; this.broken = isBroken; } + + public Station(Integer id) { + this.id = id; + } } diff --git a/src/main/java/telraam/logic/positioner/Position.java b/src/main/java/telraam/logic/positioner/Position.java index 3a4218e..43f8e26 100644 --- a/src/main/java/telraam/logic/positioner/Position.java +++ b/src/main/java/telraam/logic/positioner/Position.java @@ -1,19 +1,34 @@ package telraam.logic.positioner; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; import lombok.Getter; import lombok.Setter; -import telraam.database.models.Team; -import telraam.websocket.WebSocketMessageSingleton; -@Getter @Setter +@Getter @Setter @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class Position { - private int teamId; - private float progress; // Progress of the lap. Between 0-1 - private float speed; // Current speed. Progress / second + private final int teamId; + private double progress; // Progress of the lap. Between 0-1 + private double speed; // Current speed. progress / millisecond + private long timestamp; // Timestamp in milliseconds public Position(int teamId) { this.teamId = teamId; this.progress = 0; this.speed = 0; + this.timestamp = System.currentTimeMillis(); + } + + public Position(int teamId, double progress) { + this.teamId = teamId; + this.progress = progress; + this.speed = 0; + this.timestamp = System.currentTimeMillis(); + } + + public void update(double progress, double speed, long timestamp) { + this.progress = progress; + this.speed = speed; + this.timestamp = timestamp; } } diff --git a/src/main/java/telraam/logic/positioner/CircularQueue.java b/src/main/java/telraam/logic/positioner/nostradamus/CircularQueue.java similarity index 81% rename from src/main/java/telraam/logic/positioner/CircularQueue.java rename to src/main/java/telraam/logic/positioner/nostradamus/CircularQueue.java index 773c633..1b2faae 100644 --- a/src/main/java/telraam/logic/positioner/CircularQueue.java +++ b/src/main/java/telraam/logic/positioner/nostradamus/CircularQueue.java @@ -1,7 +1,8 @@ -package telraam.logic.positioner; +package telraam.logic.positioner.nostradamus; import java.util.LinkedList; +// LinkedList with a maximum length public class CircularQueue extends LinkedList { private final int maxSize; diff --git a/src/main/java/telraam/logic/positioner/nostradamus/DetectionList.java b/src/main/java/telraam/logic/positioner/nostradamus/DetectionList.java new file mode 100644 index 0000000..30d5848 --- /dev/null +++ b/src/main/java/telraam/logic/positioner/nostradamus/DetectionList.java @@ -0,0 +1,61 @@ +package telraam.logic.positioner.nostradamus; + +import lombok.Getter; +import telraam.database.models.Detection; +import telraam.database.models.Station; + +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class DetectionList extends ArrayList { + + private final int interval; + private final List stations; + @Getter + private Detection currentPosition; + private Timestamp newestDetection; + + public DetectionList(int interval, List stations) { + this.interval = interval; + this.stations = stations.stream().sorted(Comparator.comparing(Station::getDistanceFromStart)).map(Station::getId).toList(); + this.currentPosition = new Detection(-1, 0, -100); + this.newestDetection = new Timestamp(0); + } + + // Returns True if the added detection results in a new station + @Override + public boolean add(Detection e) { + super.add(e); + + if (e.getTimestamp().after(newestDetection)) { + newestDetection = e.getTimestamp(); + } + + if (!e.getStationId().equals(currentPosition.getStationId()) && stationAfter(currentPosition.getStationId(), e.getStationId())) { + // Possible new position + if (e.getRssi() > currentPosition.getRssi() || !inInterval(currentPosition.getTimestamp(), newestDetection)) { + // Detection stored in currentPosition will change + int oldPosition = currentPosition.getStationId(); + // Filter out old detections + removeIf(detection -> !inInterval(detection.getTimestamp(), newestDetection)); + + // Get new position + currentPosition = stream().max(Comparator.comparing(Detection::getRssi)).get(); + + return oldPosition != currentPosition.getStationId(); + } + } + + return false; + } + + private boolean stationAfter(int oldStationId, int newStationId) { + return (stations.indexOf(newStationId) - stations.indexOf(oldStationId) + stations.size()) % stations.size() > 0; + } + + private boolean inInterval(Timestamp oldest, Timestamp newest) { + return newest.getTime() - oldest.getTime() < interval; + } +} diff --git a/src/main/java/telraam/logic/positioner/nostradamus/Nostradamus.java b/src/main/java/telraam/logic/positioner/nostradamus/Nostradamus.java new file mode 100644 index 0000000..558ac38 --- /dev/null +++ b/src/main/java/telraam/logic/positioner/nostradamus/Nostradamus.java @@ -0,0 +1,154 @@ +package telraam.logic.positioner.nostradamus; + +import org.jdbi.v3.core.Jdbi; +import telraam.database.daos.BatonSwitchoverDAO; +import telraam.database.daos.StationDAO; +import telraam.database.daos.TeamDAO; +import telraam.database.models.BatonSwitchover; +import telraam.database.models.Detection; +import telraam.database.models.Station; +import telraam.database.models.Team; +import telraam.logic.positioner.Position; +import telraam.logic.positioner.PositionSender; +import telraam.logic.positioner.Positioner; + +import java.util.*; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +public class Nostradamus implements Positioner { + private static final Logger logger = Logger.getLogger(Nostradamus.class.getName()); + private final int INTERVAL_CALCULATE_MS = 500; // How often to handle new detections (in milliseconds) + private final int INTERVAL_FETCH_MS = 10000; // Interval between fetching baton switchovers (in milliseconds) + private final int INTERVAL_DETECTIONS_MS = 3000; // Amount of milliseconds to group detections by + private final int MAX_NO_DATA_MS = 30000; // Send a stationary position after receiving no station update for x amount of milliseconds + private final int MEDIAN_AMOUNT = 10; // Calculate the median running speed of the last x intervals + private final double AVERAGE_SPRINTING_SPEED_M_MS = 0.00684; // Average sprinting speed meters / milliseconds + private final int MIN_RSSI = -84; // Minimum rssi strength for a detection + private final int FINISH_OFFSET_M = 0; // Distance between the last station and the finish in meters + private final Jdbi jdbi; + private final List newDetections; // Contains not yet handled detections + private Map batonToTeam; // Baton ID to Team ID + private final Map teamData; // All team data + private final PositionSender positionSender; + private final Lock detectionLock; + private final Lock dataLock; + + public Nostradamus(Jdbi jdbi) { + this.jdbi = jdbi; + this.newDetections = new ArrayList<>(); + this.detectionLock = new ReentrantLock(); + this.dataLock = new ReentrantLock(); + + // Will be filled by fetch + this.batonToTeam = new HashMap<>(); + this.teamData = getTeamData(); + + this.positionSender = new PositionSender(); + + new Thread(this::fetch).start(); + new Thread(this::calculatePosition).start(); + } + + // Initiate the team data map + private Map getTeamData() { + List stations = jdbi.onDemand(StationDAO.class).getAll(); + stations.sort(Comparator.comparing(Station::getDistanceFromStart)); + List teams = jdbi.onDemand(TeamDAO.class).getAll(); + + return teams.stream().collect(Collectors.toMap( + Team::getId, + team -> new TeamData(team.getId(), INTERVAL_DETECTIONS_MS, stations, MEDIAN_AMOUNT, AVERAGE_SPRINTING_SPEED_M_MS, FINISH_OFFSET_M) + )); + } + + // Fetch all baton switchovers and replace the current one if there are any changes + private void fetch() { + List switchovers = jdbi.onDemand(BatonSwitchoverDAO.class).getAll(); + + Map batonToTeam = switchovers.stream().sorted( + Comparator.comparing(BatonSwitchover::getTimestamp) + ).collect(Collectors.toMap( + BatonSwitchover::getNewBatonId, + BatonSwitchover::getTeamId, + (existing, replacement) -> replacement + )); + + if (!this.batonToTeam.equals(batonToTeam)) { + dataLock.lock(); + this.batonToTeam = batonToTeam; + dataLock.unlock(); + } + + // Sleep tight + try { + Thread.sleep(INTERVAL_FETCH_MS); + } catch (InterruptedException e) { + logger.severe(e.getMessage()); + } + } + + // handle all new detections and update positions accordingly + private void calculatePosition() { + Set changedTeams = new HashSet<>(); // List of teams that have changed station + while (true) { + changedTeams.clear(); + dataLock.lock(); + detectionLock.lock(); + for (Detection detection: newDetections) { + if (batonToTeam.containsKey(detection.getBatonId())) { + Integer teamId = batonToTeam.get(detection.getBatonId()); + if (teamData.get(teamId).addDetection(detection)) { + changedTeams.add(teamId); + } + } + } + newDetections.clear(); + detectionLock.unlock(); // Use lock as short as possible + dataLock.unlock(); + + if (!changedTeams.isEmpty()) { + // Update + for (Integer teamId: changedTeams) { + teamData.get(teamId).updatePosition(); + } + + // Send new data to the websocket + positionSender.send( + changedTeams.stream().map(team -> teamData.get(team).getPosition()).toList() + ); + } + + // Send a stationary position if no new station data was received recently + long now = System.currentTimeMillis(); + for (Map.Entry entry: teamData.entrySet()) { + if (now - entry.getValue().getPreviousStationArrival() > MAX_NO_DATA_MS) { + positionSender.send( + Collections.singletonList(new Position( + entry.getKey(), + entry.getValue().getPosition().getProgress() + )) + ); + } + } + + // Goodnight + try { + Thread.sleep(INTERVAL_CALCULATE_MS); + } catch (InterruptedException e) { + logger.severe(e.getMessage()); + } + } + } + + @Override + public void handle(Detection detection) { + if (detection.getRssi() > MIN_RSSI) { + detectionLock.lock(); + newDetections.add(detection); + detectionLock.unlock(); + } + } +} diff --git a/src/main/java/telraam/logic/positioner/nostradamus/StationData.java b/src/main/java/telraam/logic/positioner/nostradamus/StationData.java new file mode 100644 index 0000000..fbc9d8d --- /dev/null +++ b/src/main/java/telraam/logic/positioner/nostradamus/StationData.java @@ -0,0 +1,38 @@ +package telraam.logic.positioner.nostradamus; + +import telraam.database.models.Station; + +import java.util.ArrayList; +import java.util.List; + +// Record containing all data necessary for TeamData +public record StationData( + Station station, // The station + Station nextStation, // The next station + List times, // List containing the times (in ms) that was needed to run from this station to the next one. + int index, // Index of this station when sorting a station list by distanceFromStart + float currentProgress, // The progress value of this station + float nextProgress // The progress value of the next station +) { + public StationData() { + this( + new Station(-10), + new Station(-9), + new ArrayList<>(0), + -10, + 0F, + 0F + ); + } + + public StationData(List stations, int index, int averageAmount, int totalDistance) { + this( + stations.get(index), + stations.get((index + 1) % stations.size()), + new CircularQueue<>(averageAmount), + index, + (float) (stations.get(index).getDistanceFromStart() / totalDistance), + (float) (stations.get((index + 1) % stations.size()).getDistanceFromStart() / totalDistance) + ); + } +} diff --git a/src/main/java/telraam/logic/positioner/nostradamus/TeamData.java b/src/main/java/telraam/logic/positioner/nostradamus/TeamData.java new file mode 100644 index 0000000..6fba92d --- /dev/null +++ b/src/main/java/telraam/logic/positioner/nostradamus/TeamData.java @@ -0,0 +1,119 @@ +package telraam.logic.positioner.nostradamus; + +import lombok.Getter; +import telraam.database.models.Detection; +import telraam.database.models.Station; +import telraam.logic.positioner.Position; + +import java.util.*; +import java.util.stream.Collectors; + +public class TeamData { + private final DetectionList detections; // List with all relevant detections + private final Map stations; // Station list + private StationData currentStation; // Current station location + private StationData previousStation; // Previous station location + @Getter + private long previousStationArrival; // Arrival time of previous station. Used to calculate the average times + private final int totalDistance; // Total distance of the track + private final float maxDeviance; // Maximum deviance the animation can have from the reality + @Getter + private final Position position; // Data to send to the websocket + + + public TeamData(int teamId, int interval, List stations, int averageAmount, double sprintingSpeed, int finishOffset) { + stations.sort(Comparator.comparing(Station::getDistanceFromStart)); + this.totalDistance = (int) (stations.get(stations.size() - 1).getDistanceFromStart() + finishOffset); + this.stations = stations.stream().collect(Collectors.toMap( + Station::getId, + station -> new StationData( + stations, + stations.indexOf(station), + averageAmount, + totalDistance + ) + )); + // Pre-populate with some default values + this.stations.forEach((stationId, stationData) -> stationData.times().add( + (long) (((stationData.nextStation().getDistanceFromStart() - stationData.station().getDistanceFromStart() + totalDistance) % totalDistance) / sprintingSpeed) + )); + this.detections = new DetectionList(interval, stations); + this.previousStationArrival = System.currentTimeMillis(); + this.currentStation = new StationData(); // Will never trigger `isNextStation` for the first station + this.position = new Position(teamId); + this.maxDeviance = (float) 1 / stations.size(); + } + + // Add a new detection + // Returns true if the team is at a new station + public boolean addDetection(Detection e) { + boolean newStation = detections.add(e); + + if ((newStation && isForwardStation(currentStation.index(), stations.get(e.getStationId()).index())) || currentStation.index() == -10) { + // Is at a new station that is in front of the previous one. + previousStation = currentStation; + currentStation = stations.get(e.getStationId()); + long now = System.currentTimeMillis(); + if (isNextStation()) { + // Only add the time if it goes forward by exactly one station + previousStation.times().add(now - previousStationArrival); + } + previousStationArrival = now; + + return true; + } + + return false; + } + + private boolean isForwardStation(int oldStation, int newStation) { + int stationDiff = (newStation - oldStation + stations.size()) % stations.size(); + return stationDiff < 3; + } + + private boolean isNextStation() { + return Objects.equals(previousStation.nextStation().getId(), currentStation.station().getId()); + } + + private double normalize(double number) { + return (number + 1) % 1; + } + + // Update the position data + public void updatePosition() { + long currentTime = System.currentTimeMillis(); + + // Animation is currently at progress x + long milliSecondsSince = currentTime - position.getTimestamp(); + double theoreticalProgress = normalize(position.getProgress() + (position.getSpeed() * milliSecondsSince)); + + // Arrive at next station at timestamp y and progress z + double median = getMedian(); + double nextStationArrival = currentTime + median; + double goalProgress = currentStation.nextProgress(); + + double speed, progress; + // Determine whether to speed up / slow down the animation or teleport it + double difference = normalize(currentStation.currentProgress() - theoreticalProgress); + if ((difference >= maxDeviance && difference <= 1 - maxDeviance)) { + // Animation was too far behind or ahead so teleport + progress = currentStation.currentProgress(); + speed = normalize(currentStation.nextProgress() - progress) / median; + } else { + // Animation is close enough, adjust so that we're synced at the next station + progress = theoreticalProgress; + speed = normalize(goalProgress - theoreticalProgress) / (nextStationArrival - currentTime); + } + + position.update(progress, speed, currentTime); + } + + // Get the medium of the average times + private long getMedian() { + List sortedList = new ArrayList<>(currentStation.times()); + Collections.sort(sortedList); + + int size = sortedList.size(); + return size % 2 == 0 ? (sortedList.get(size / 2 - 1) + sortedList.get(size / 2)) / 2 : (sortedList.get(size / 2)); + } +} diff --git a/src/main/java/telraam/logic/positioner/simple/SimplePositioner.java b/src/main/java/telraam/logic/positioner/simple/SimplePositioner.java deleted file mode 100644 index 9958d1e..0000000 --- a/src/main/java/telraam/logic/positioner/simple/SimplePositioner.java +++ /dev/null @@ -1,103 +0,0 @@ -package telraam.logic.positioner.simple; - -import org.jdbi.v3.core.Jdbi; -import telraam.database.daos.BatonSwitchoverDAO; -import telraam.database.daos.StationDAO; -import telraam.database.daos.TeamDAO; -import telraam.database.models.BatonSwitchover; -import telraam.database.models.Detection; -import telraam.database.models.Station; -import telraam.database.models.Team; -import telraam.logic.positioner.CircularQueue; -import telraam.logic.positioner.Position; -import telraam.logic.positioner.PositionSender; -import telraam.logic.positioner.Positioner; - -import java.util.*; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class SimplePositioner implements Positioner { - private static final Logger logger = Logger.getLogger(SimplePositioner.class.getName()); - private final int QUEUE_SIZE = 50; - private final int MIN_RSSI = -85; - private final int DEBOUNCE_TIMEOUT = 1; - private boolean debounceScheduled; - private final ScheduledExecutorService scheduler; - private final PositionSender positionSender; - private final Map batonIdToTeam; - private final Map> teamDetections; - private final List stations; - private final Map teamPositions; - - public SimplePositioner(Jdbi jdbi) { - this.debounceScheduled = false; - this.scheduler = Executors.newScheduledThreadPool(1); - this.positionSender = new PositionSender(); - this.batonIdToTeam = new HashMap<>(); - this.teamDetections = new HashMap<>(); - this.teamPositions = new HashMap<>(); - - TeamDAO teamDAO = jdbi.onDemand(TeamDAO.class); - List teams = teamDAO.getAll(); - for (Team team : teams) { - teamDetections.put(team.getId(), new CircularQueue<>(QUEUE_SIZE)); - teamPositions.put(team.getId(), new Position(team.getId())); - } - List switchovers = jdbi.onDemand(BatonSwitchoverDAO.class).getAll(); - switchovers.sort(Comparator.comparing(BatonSwitchover::getTimestamp)); - - for (BatonSwitchover switchover : switchovers) { - batonIdToTeam.put(switchover.getNewBatonId(), teamDAO.getById(switchover.getTeamId()).get()); - } - - List stationList = jdbi.onDemand(StationDAO.class).getAll(); - stationList.sort(Comparator.comparing(Station::getDistanceFromStart)); - stations = stationList.stream().map(Station::getId).toList(); - } - - public void calculatePositions() { - logger.info("SimplePositioner: Calculating positions..."); - for (Map.Entry> entry : teamDetections.entrySet()) { - List detections = teamDetections.get(entry.getKey()); - detections.sort(Comparator.comparing(Detection::getTimestamp)); - - int currentStationRssi = MIN_RSSI; - int currentStationPosition = 0; - for (Detection detection : detections) { - if (detection.getRssi() > currentStationRssi) { - currentStationRssi = detection.getRssi(); - currentStationPosition = detection.getStationId(); - } - } - - float progress = ((float) 100 / stations.size()) * currentStationPosition; - teamPositions.get(entry.getKey()).setProgress(progress); - } - - positionSender.send(teamPositions.values().stream().toList()); - logger.info("SimplePositioner: Done calculating positions"); - } - - public synchronized void handle(Detection detection) { - Team team = batonIdToTeam.get(detection.getBatonId()); - teamDetections.get(team.getId()).add(detection); - - if (!debounceScheduled) { - debounceScheduled = true; - scheduler.schedule(() -> { - try { - calculatePositions(); - } catch (Exception e) { - logger.log(Level.SEVERE, e.getMessage(), e); - } - debounceScheduled = false; - }, DEBOUNCE_TIMEOUT, TimeUnit.SECONDS); - } - } -} diff --git a/src/main/java/telraam/station/websocket/WebsocketFetcher.java b/src/main/java/telraam/station/websocket/WebsocketFetcher.java index 285ad15..62e9996 100644 --- a/src/main/java/telraam/station/websocket/WebsocketFetcher.java +++ b/src/main/java/telraam/station/websocket/WebsocketFetcher.java @@ -90,7 +90,6 @@ public void fetch() { return; } - WebsocketClient websocketClient = new WebsocketClient(url); websocketClient.addOnOpenHandler(() -> { websocketClient.sendMessage(wsMessageEncoded); @@ -104,6 +103,15 @@ public void fetch() { } this.fetch(); }); + websocketClient.addOnCloseHandler(() -> { + this.logger.severe(String.format("Websocket for station %s got closed", station.getName())); + try { + Thread.sleep(Fetcher.ERROR_TIMEOUT_MS); + } catch (InterruptedException e) { + logger.severe(e.getMessage()); + } + this.fetch(); + }); websocketClient.addMessageHandler((String msg) -> { //Insert detections List new_detections = new ArrayList<>(); diff --git a/src/main/java/telraam/websocket/WebSocketMessage.java b/src/main/java/telraam/websocket/WebSocketMessage.java index 0b4200f..b280ec2 100644 --- a/src/main/java/telraam/websocket/WebSocketMessage.java +++ b/src/main/java/telraam/websocket/WebSocketMessage.java @@ -1,9 +1,10 @@ package telraam.websocket; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; -@Getter @Setter +@Getter @Setter @NoArgsConstructor public class WebSocketMessage { private String topic; private T data;