Added branch endpoints and some minor logic updates.
This commit is contained in:
parent
108edac5e7
commit
591f46d60d
|
@ -11,6 +11,8 @@ import java.util.Optional;
|
|||
@Repository
|
||||
public interface BranchRepository extends JpaRepository<Branch, Long> {
|
||||
Optional<Branch> findByIdAndRailSystem(long id, RailSystem railSystem);
|
||||
Optional<Branch> findByIdAndRailSystemId(long id, long railSystemId);
|
||||
Optional<Branch> findByNameAndRailSystem(String name, RailSystem railSystem);
|
||||
List<Branch> findAllByRailSystemOrderByName(RailSystem railSystem);
|
||||
List<Branch> findAllByNameAndRailSystem(String name, RailSystem railSystem);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package nl.andrewl.railsignalapi.rest;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import nl.andrewl.railsignalapi.rest.dto.BranchResponse;
|
||||
import nl.andrewl.railsignalapi.service.BranchService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/railSystems/{rsId}/branches")
|
||||
@RequiredArgsConstructor
|
||||
public class BranchesController {
|
||||
private final BranchService branchService;
|
||||
|
||||
@GetMapping
|
||||
public List<BranchResponse> getAllBranches(@PathVariable long rsId) {
|
||||
return branchService.getAllBranches(rsId);
|
||||
}
|
||||
|
||||
@DeleteMapping(path = "/{branchId}")
|
||||
public ResponseEntity<?> deleteBranch(@PathVariable long rsId, @PathVariable long branchId) {
|
||||
branchService.deleteBranch(rsId, branchId);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor;
|
|||
import nl.andrewl.railsignalapi.rest.dto.SignalCreationPayload;
|
||||
import nl.andrewl.railsignalapi.rest.dto.SignalResponse;
|
||||
import nl.andrewl.railsignalapi.service.SignalService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -28,4 +29,10 @@ public class SignalsApiController {
|
|||
public SignalResponse getSignal(@PathVariable long rsId, @PathVariable long sigId) {
|
||||
return signalService.getSignal(rsId, sigId);
|
||||
}
|
||||
|
||||
@DeleteMapping(path = "/{sigId}")
|
||||
public ResponseEntity<?> deleteSignal(@PathVariable long rsId, @PathVariable long sigId) {
|
||||
signalService.deleteSignal(rsId, sigId);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package nl.andrewl.railsignalapi.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import nl.andrewl.railsignalapi.dao.BranchRepository;
|
||||
import nl.andrewl.railsignalapi.dao.RailSystemRepository;
|
||||
import nl.andrewl.railsignalapi.rest.dto.BranchResponse;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class BranchService {
|
||||
private final BranchRepository branchRepository;
|
||||
private final RailSystemRepository railSystemRepository;
|
||||
|
||||
@Transactional
|
||||
public void deleteBranch(long rsId, long branchId) {
|
||||
var branch = branchRepository.findByIdAndRailSystemId(branchId, rsId)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
if (!branch.getSignalConnections().isEmpty()) {
|
||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Branch should not be connected to any signals.");
|
||||
}
|
||||
branchRepository.delete(branch);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<BranchResponse> getAllBranches(long rsId) {
|
||||
var rs = railSystemRepository.findById(rsId)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
return branchRepository.findAllByRailSystemOrderByName(rs).stream()
|
||||
.map(BranchResponse::new)
|
||||
.toList();
|
||||
}
|
||||
}
|
|
@ -33,7 +33,8 @@ public class SignalService {
|
|||
private final SignalRepository signalRepository;
|
||||
private final BranchRepository branchRepository;
|
||||
|
||||
private final Map<Long, WebSocketSession> signalWebSocketSessions = new ConcurrentHashMap<>();
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
private final Map<WebSocketSession, Set<Long>> signalWebSocketSessions = new ConcurrentHashMap<>();
|
||||
|
||||
@Transactional
|
||||
public SignalResponse createSignal(long rsId, SignalCreationPayload payload) {
|
||||
|
@ -60,17 +61,42 @@ public class SignalService {
|
|||
return new SignalResponse(signal);
|
||||
}
|
||||
|
||||
public void registerSignalWebSocketSession(long signalId, WebSocketSession session) {
|
||||
this.signalWebSocketSessions.put(signalId, session);
|
||||
@Transactional(readOnly = true)
|
||||
public void registerSignalWebSocketSession(Set<Long> signalIds, WebSocketSession session) {
|
||||
this.signalWebSocketSessions.put(session, signalIds);
|
||||
// Instantly send a data packet so that the signals are up-to-date.
|
||||
for (var signalId : signalIds) {
|
||||
var signal = signalRepository.findById(signalId)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid signal id."));
|
||||
for (var branchConnection : signal.getBranchConnections()) {
|
||||
try {
|
||||
session.sendMessage(new TextMessage(mapper.writeValueAsString(
|
||||
new BranchUpdateMessage(
|
||||
branchConnection.getBranch().getId(),
|
||||
branchConnection.getBranch().getStatus().name()
|
||||
)
|
||||
)));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void deregisterSignalWebSocketSession(long signalId) {
|
||||
this.signalWebSocketSessions.remove(signalId);
|
||||
public void deregisterSignalWebSocketSession(WebSocketSession session) {
|
||||
this.signalWebSocketSessions.remove(session);
|
||||
}
|
||||
|
||||
public WebSocketSession getSignalWebSocketSession(long signalId) {
|
||||
for (var entry : signalWebSocketSessions.entrySet()) {
|
||||
if (entry.getValue().contains(signalId)) return entry.getKey();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void handleSignalUpdate(long signalId, SignalUpdateMessage updateMessage) {
|
||||
var signal = signalRepository.findById(signalId)
|
||||
public void handleSignalUpdate(SignalUpdateMessage updateMessage) {
|
||||
var signal = signalRepository.findById(updateMessage.signalId())
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
Branch fromBranch = null;
|
||||
Branch toBranch = null;
|
||||
|
@ -102,15 +128,21 @@ public class SignalService {
|
|||
branchRepository.save(fromBranch);
|
||||
broadcastToConnectedSignals(fromBranch);
|
||||
}
|
||||
} else if (updateType == SignalUpdateType.BEGIN) {
|
||||
if (fromBranch.getStatus() != BranchStatus.OCCUPIED) {
|
||||
log.info("Updating branch {} status from {} to {}.", fromBranch.getName(), fromBranch.getStatus(), BranchStatus.OCCUPIED);
|
||||
fromBranch.setStatus(BranchStatus.OCCUPIED);
|
||||
branchRepository.save(fromBranch);
|
||||
broadcastToConnectedSignals(fromBranch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastToConnectedSignals(Branch branch) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try {
|
||||
WebSocketMessage<String> msg = new TextMessage(mapper.writeValueAsString(new BranchUpdateMessage(branch.getId(), branch.getStatus().name())));
|
||||
signalRepository.findAllConnectedToBranch(branch).stream()
|
||||
.map(s -> signalWebSocketSessions.get(s.getId()))
|
||||
.map(s -> getSignalWebSocketSession(s.getId()))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(session -> {
|
||||
try {
|
||||
|
@ -140,4 +172,11 @@ public class SignalService {
|
|||
.map(SignalResponse::new)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteSignal(long rsId, long sigId) {
|
||||
var s = signalRepository.findByIdAndRailSystemId(sigId, rsId)
|
||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
signalRepository.delete(s);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package nl.andrewl.railsignalapi.websocket;
|
||||
|
||||
public record SignalUpdateMessage(
|
||||
long signalId,
|
||||
long fromBranchId,
|
||||
long toBranchId,
|
||||
String type
|
||||
|
|
|
@ -10,6 +10,9 @@ import org.springframework.web.socket.TextMessage;
|
|||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
|
@ -24,27 +27,23 @@ public class SignalWebSocketHandler extends TextWebSocketHandler {
|
|||
session.close(CloseStatus.PROTOCOL_ERROR);
|
||||
return;
|
||||
}
|
||||
long signalId = Long.parseLong(signalIdHeader);
|
||||
session.getAttributes().put("signalId", signalId);
|
||||
signalService.registerSignalWebSocketSession(signalId, session);
|
||||
log.info("Connection established with signal {}.", signalId);
|
||||
Set<Long> ids = new HashSet<>();
|
||||
for (var idStr : signalIdHeader.split(",")) {
|
||||
ids.add(Long.parseLong(idStr.trim()));
|
||||
}
|
||||
signalService.registerSignalWebSocketSession(ids, session);
|
||||
log.info("Connection established with signals {}.", ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
|
||||
var msg = mapper.readValue(message.getPayload(), SignalUpdateMessage.class);
|
||||
Long signalId = (Long) session.getAttributes().get("signalId");
|
||||
if (signalId == null) {
|
||||
log.warn("Got text message from a websocket session that did not establish a signalId session attribute.");
|
||||
} else {
|
||||
log.info("Received update from signal {}.", signalId);
|
||||
signalService.handleSignalUpdate(signalId, msg);
|
||||
}
|
||||
signalService.handleSignalUpdate(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
|
||||
signalService.deregisterSignalWebSocketSession((Long) session.getAttributes().get("signalId"));
|
||||
log.info("Closed connection to signal {}. Status: {}", session.getAttributes().get("signalId"), status.toString());
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
|
||||
signalService.deregisterSignalWebSocketSession(session);
|
||||
log.info("Closed connection {}. Status: {}", session.getId(), status.toString());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue