Skip to content

Commit

Permalink
Merge pull request #123 from 12urenloop/simple_positioner
Browse files Browse the repository at this point in the history
Simple positioner
  • Loading branch information
Topvennie authored Mar 29, 2024
2 parents a85e4d4 + f71ad1b commit 3a1953b
Show file tree
Hide file tree
Showing 18 changed files with 234 additions and 26 deletions.
15 changes: 11 additions & 4 deletions src/main/java/telraam/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
import telraam.database.daos.*;
import telraam.database.models.Station;
import telraam.healthchecks.TemplateHealthCheck;
import telraam.logic.Lapper;
import telraam.logic.external.ExternalLapper;
import telraam.logic.robust.RobustLapper;
import telraam.logic.lapper.Lapper;
import telraam.logic.lapper.external.ExternalLapper;
import telraam.logic.lapper.robust.RobustLapper;
import telraam.logic.positioner.Positioner;
import telraam.logic.positioner.simple.SimplePositioner;
import telraam.station.Fetcher;
import telraam.util.AcceptedLapsUtil;
import telraam.websocket.WebSocketConnection;
Expand Down Expand Up @@ -131,10 +133,15 @@ public void run(AppConfiguration configuration, Environment environment) throws
lapper.registerAPI(jersey);
}

// Set up positioners
Set<Positioner> positioners = new HashSet<>();

positioners.add(new SimplePositioner(this.database));

// Start fetch thread for each station
StationDAO stationDAO = this.database.onDemand(StationDAO.class);
for (Station station : stationDAO.getAll()) {
new Thread(() -> new Fetcher(this.database, station, lappers).fetch()).start();
new Thread(() -> new Fetcher(this.database, station, lappers, positioners).fetch()).start();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package telraam.logic;
package telraam.logic.lapper;

import io.dropwizard.jersey.setup.JerseyEnvironment;
import telraam.database.models.Detection;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package telraam.logic.external;
package telraam.logic.lapper.external;

import io.dropwizard.jersey.setup.JerseyEnvironment;
import org.jdbi.v3.core.Jdbi;
Expand All @@ -7,8 +7,8 @@
import telraam.database.models.Detection;
import telraam.database.models.Lap;
import telraam.database.models.LapSource;
import telraam.logic.Lapper;
import telraam.logic.external.models.ExternalLapperTeamLaps;
import telraam.logic.lapper.Lapper;
import telraam.logic.lapper.external.models.ExternalLapperTeamLaps;

import java.sql.Timestamp;
import java.util.Comparator;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package telraam.logic.external;
package telraam.logic.lapper.external;

import io.swagger.v3.oas.annotations.Operation;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import telraam.logic.external.models.ExternalLapperStats;
import telraam.logic.external.models.ExternalLapperTeamLaps;
import telraam.logic.lapper.external.models.ExternalLapperStats;
import telraam.logic.lapper.external.models.ExternalLapperTeamLaps;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package telraam.logic.external.models;
package telraam.logic.lapper.external.models;

import lombok.Getter;
import lombok.Setter;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package telraam.logic.external.models;
package telraam.logic.lapper.external.models;

import lombok.Getter;
import lombok.Setter;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package telraam.logic.external.models;
package telraam.logic.lapper.external.models;

import lombok.Getter;
import lombok.Setter;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package telraam.logic.robust;
package telraam.logic.lapper.robust;

import io.dropwizard.jersey.setup.JerseyEnvironment;
import org.jdbi.v3.core.Jdbi;
Expand All @@ -10,7 +10,7 @@
import telraam.database.models.Lap;
import telraam.database.models.LapSource;
import telraam.database.models.Station;
import telraam.logic.Lapper;
import telraam.logic.lapper.Lapper;

import java.sql.Timestamp;
import java.util.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package telraam.logic.simple;
package telraam.logic.lapper.simple;

import io.dropwizard.jersey.setup.JerseyEnvironment;
import org.jdbi.v3.core.Jdbi;
import telraam.database.daos.LapDAO;
import telraam.database.daos.LapSourceDAO;
import telraam.database.models.*;
import telraam.logic.Lapper;
import telraam.logic.lapper.Lapper;

import java.util.ArrayList;
import java.util.HashMap;
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/telraam/logic/positioner/CircularQueue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package telraam.logic.positioner;

import java.util.LinkedList;

public class CircularQueue<T> extends LinkedList<T> {

private final int maxSize;
public CircularQueue(int maxSize) {
this.maxSize = maxSize;
}

@Override
public boolean add(T e) {
if (size() >= this.maxSize) {
removeFirst();
}

return super.add(e);
}

}
19 changes: 19 additions & 0 deletions src/main/java/telraam/logic/positioner/Position.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package telraam.logic.positioner;

import lombok.Getter;
import lombok.Setter;
import telraam.database.models.Team;
import telraam.websocket.WebSocketMessageSingleton;

@Getter @Setter
public class Position {
private int teamId;
private float progress; // Progress of the lap. Between 0-1
private float speed; // Current speed. Progress / second

public Position(int teamId) {
this.teamId = teamId;
this.progress = 0;
this.speed = 0;
}
}
20 changes: 20 additions & 0 deletions src/main/java/telraam/logic/positioner/PositionSender.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package telraam.logic.positioner;

import telraam.websocket.WebSocketMessage;
import telraam.websocket.WebSocketMessageSingleton;

import java.util.List;

public class PositionSender {
private final WebSocketMessage<List<Position>> message = new WebSocketMessage<>();

public PositionSender() {
this.message.setTopic("position");
}

public void send(List<Position> positions) {
this.message.setData(positions);
WebSocketMessageSingleton.getInstance().sendToAll(this.message);
}

}
8 changes: 8 additions & 0 deletions src/main/java/telraam/logic/positioner/Positioner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package telraam.logic.positioner;

import telraam.database.models.Detection;

public interface Positioner {
void handle(Detection detection);

}
104 changes: 104 additions & 0 deletions src/main/java/telraam/logic/positioner/simple/SimplePositioner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
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.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

public class SimplePositioner implements Positioner {
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 static final Logger logger = Logger.getLogger(SimplePositioner.class.getName());
private final PositionSender positionSender;
private final Map<Integer, Team> batonIdToTeam;
private final Map<Team, CircularQueue<Detection>> teamDetections;
private final List<Integer> stations;
private final Map<Team, Position> 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<Team> teams = teamDAO.getAll();
for (Team team: teams) {
teamDetections.put(team, new CircularQueue<>(QUEUE_SIZE));
teamPositions.put(team, new Position(team.getId()));
}
List<BatonSwitchover> 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<Station> 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<Team, CircularQueue<Detection>> entry: teamDetections.entrySet()) {
List<Detection> 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 void handle(Detection detection) {
Team team = batonIdToTeam.get(detection.getBatonId());
teamDetections.get(team).add(detection);

if (! debounceScheduled) {
debounceScheduled = true;
scheduler.schedule(() -> {
try {
calculatePositions();
} catch (Exception e) {
logger.severe(e.getMessage());
}
debounceScheduled = false;
}, DEBOUNCE_TIMEOUT, TimeUnit.SECONDS);
}
}

}
12 changes: 9 additions & 3 deletions src/main/java/telraam/station/Fetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
import telraam.database.models.Baton;
import telraam.database.models.Detection;
import telraam.database.models.Station;
import telraam.logic.Lapper;
import telraam.logic.lapper.Lapper;
import telraam.logic.positioner.Positioner;
import telraam.station.models.RonnyDetection;
import telraam.station.models.RonnyResponse;

Expand All @@ -29,6 +30,7 @@

public class Fetcher {
private final Set<Lapper> lappers;
private final Set<Positioner> positioners;
private Station station;

private final BatonDAO batonDAO;
Expand All @@ -48,11 +50,12 @@ public class Fetcher {
private final static int IDLE_TIMEOUT_MS = 4000; // Wait 4 seconds


public Fetcher(Jdbi database, Station station, Set<Lapper> lappers) {
public Fetcher(Jdbi database, Station station, Set<Lapper> lappers, Set<Positioner> positioners) {
this.batonDAO = database.onDemand(BatonDAO.class);
this.detectionDAO = database.onDemand(DetectionDAO.class);
this.stationDAO = database.onDemand(StationDAO.class);
this.lappers = lappers;
this.positioners = positioners;

this.station = station;
}
Expand Down Expand Up @@ -158,7 +161,10 @@ public void fetch() {
}
if (!new_detections.isEmpty()) {
detectionDAO.insertAll(new_detections);
new_detections.forEach((detection) -> lappers.forEach((lapper) -> lapper.handle(detection)));
new_detections.forEach((detection) -> {
lappers.forEach((lapper) -> lapper.handle(detection));
positioners.forEach((positioner) -> positioner.handle(detection));
});
}

this.logger.finer("Fetched " + detections.size() + " detections from " + station.getName() + ", Saved " + new_detections.size());
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/telraam/websocket/WebSocketMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package telraam.websocket;

import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class WebSocketMessage<T> {
private String topic;
private T data;
}
19 changes: 16 additions & 3 deletions src/main/java/telraam/websocket/WebSocketMessageSingleton.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
package telraam.websocket;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;

@NoArgsConstructor
public class WebSocketMessageSingleton {
private static final Logger logger = Logger.getLogger(WebSocketMessageSingleton.class.getName());

@Getter
private static final WebSocketMessageSingleton instance = new WebSocketMessageSingleton();
private static final Set<WebSocketConnection> registeredConnections = new HashSet<>();

private WebSocketMessageSingleton() {
}
private final ObjectMapper mapper = new ObjectMapper();

public void registerConnection(WebSocketConnection conn) {
boolean modified = registeredConnections.add(conn);
Expand All @@ -34,4 +38,13 @@ public void sendToAll(String s) {
logger.finest("Sending \"%s\" to all registered WebSocketConnection instances".formatted(s));
registeredConnections.forEach(conn -> conn.send(s));
}

public void sendToAll(WebSocketMessage<?> message) {
try {
String json = mapper.writeValueAsString(message);
this.sendToAll(json);
} catch (JsonProcessingException e) {
logger.severe("Json conversion error for \"%s\"".formatted(message.toString()));
}
}
}
Loading

0 comments on commit 3a1953b

Please sign in to comment.