Version bump, and fixed some small issues with rendering and creating switches.

This commit is contained in:
Andrew Lalis 2022-06-02 10:07:59 +02:00
parent cb8eed835a
commit 99f438161f
8 changed files with 72 additions and 20 deletions

View File

@ -10,7 +10,7 @@
</parent> </parent>
<groupId>nl.andrewl</groupId> <groupId>nl.andrewl</groupId>
<artifactId>rail-signal-api</artifactId> <artifactId>rail-signal-api</artifactId>
<version>2.1.0</version> <version>2.2.0</version>
<name>rail-signal-api</name> <name>rail-signal-api</name>
<description>A simple API for tracking rail traffic in signalled blocks.</description> <description>A simple API for tracking rail traffic in signalled blocks.</description>
<properties> <properties>

View File

@ -70,7 +70,7 @@ export default {
message: "Are you sure you want to remove this component? This cannot be undone.", message: "Are you sure you want to remove this component? This cannot be undone.",
cancel: true cancel: true
}).onOk(() => { }).onOk(() => {
removeComponent(this.railSystem, component.id) removeComponent(this.rsStore.selectedRailSystem, component.id)
.then(() => { .then(() => {
this.quasar.notify({ this.quasar.notify({
color: "positive", color: "positive",

View File

@ -127,7 +127,19 @@ export default {
}, },
methods: { methods: {
setActiveSwitchConfig(configId) { setActiveSwitchConfig(configId) {
updateSwitchConfiguration(this.railSystem, this.sw, configId); updateSwitchConfiguration(this.rsStore.selectedRailSystem, this.sw, configId)
.then(() => {
this.quasar.notify({
color: "positive",
message: "Sent switch configuration update request."
});
})
.catch(error => {
this.quasar.notify({
color: "negative",
message: "An error occurred: " + error.response.data.message
});
});
}, },
isConfigActive(config) { isConfigActive(config) {
return this.sw.activeConfiguration !== null && this.sw.activeConfiguration.id === config.id; return this.sw.activeConfiguration !== null && this.sw.activeConfiguration.id === config.id;

View File

@ -23,3 +23,28 @@ export function mulberry32(a) {
return ((t ^ t >>> 14) >>> 0) / 4294967296; return ((t ^ t >>> 14) >>> 0) / 4294967296;
} }
} }
export function sortPoints(points) {
points = points.splice(0);
const p0 = {};
p0.y = Math.min.apply(null, points.map(p=>p.y));
p0.x = Math.max.apply(null, points.filter(p=>p.y === p0.y).map(p=>p.x));
points.sort((a,b)=>angleCompare(p0, a, b));
return points;
}
function angleCompare(p0, a, b) {
const left = isLeft(p0, a, b);
if (left === 0) return distCompare(p0, a, b);
return left;
}
function isLeft(p0, a, b) {
return (a.x-p0.x)*(b.y-p0.y) - (b.x-p0.x)*(a.y-p0.y);
}
function distCompare(p0, a, b) {
const distA = (p0.x-a.x)*(p0.x-a.x) + (p0.y-a.y)*(p0.y-a.y);
const distB = (p0.x-b.x)*(p0.x-b.x) + (p0.y-b.y)*(p0.y-b.y);
return distA - distB;
}

View File

@ -3,7 +3,7 @@ Helper functions to actually perform rendering of different components.
*/ */
import { getScaleFactor, getWorldTransform, isComponentHovered, isComponentSelected } from "./mapRenderer"; import { getScaleFactor, getWorldTransform, isComponentHovered, isComponentSelected } from "./mapRenderer";
import { circle, roundedRect } from "./canvasUtils"; import { circle, roundedRect, sortPoints } from "./canvasUtils";
import randomColor from "randomcolor"; import randomColor from "randomcolor";
export function drawMap(ctx, rs) { export function drawMap(ctx, rs) {
@ -16,6 +16,7 @@ export function drawMap(ctx, rs) {
function drawSegments(ctx, rs) { function drawSegments(ctx, rs) {
const segmentPoints = new Map(); const segmentPoints = new Map();
// Gather for each segment a set of points representing its bounds.
rs.segments.forEach(segment => segmentPoints.set(segment.id, [])); rs.segments.forEach(segment => segmentPoints.set(segment.id, []));
for (let i = 0; i < rs.components.length; i++) { for (let i = 0; i < rs.components.length; i++) {
const c = rs.components[i]; const c = rs.components[i];
@ -25,6 +26,11 @@ function drawSegments(ctx, rs) {
} }
} }
} }
// Sort the points to make regular convex polygons.
for (let i = 0; i < rs.segments.length; i++) {
const unsortedPoints = segmentPoints.get(rs.segments[i].id);
segmentPoints.set(rs.segments[i].id, sortPoints(unsortedPoints));
}
for (let i = 0; i < rs.segments.length; i++) { for (let i = 0; i < rs.segments.length; i++) {
const color = randomColor({ luminosity: 'light', format: 'rgb', seed: rs.segments[i].id }); const color = randomColor({ luminosity: 'light', format: 'rgb', seed: rs.segments[i].id });

View File

@ -1,14 +1,15 @@
package nl.andrewl.railsignalapi.rest.dto.component.in; package nl.andrewl.railsignalapi.rest.dto.component.in;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
public class SwitchPayload extends ComponentPayload { public class SwitchPayload extends ComponentPayload {
@NotEmpty @Size(min = 2, max = 10) @NotNull @Size(max = 10)
public SwitchConfigurationPayload[] possibleConfigurations; public SwitchConfigurationPayload[] possibleConfigurations;
public static class SwitchConfigurationPayload { public static class SwitchConfigurationPayload {
@NotEmpty @Size(min = 2, max = 10) @NotEmpty @Size(min = 2, max = 2)
public NodePayload[] nodes; public NodePayload[] nodes;
public static class NodePayload { public static class NodePayload {

View File

@ -87,9 +87,6 @@ public class ComponentCreationService {
} }
s.getPossibleConfigurations().add(new SwitchConfiguration(s, pathNodes)); s.getPossibleConfigurations().add(new SwitchConfiguration(s, pathNodes));
} }
if (s.getPossibleConfigurations().size() < 2) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "At least two switch configurations are needed.");
}
return s; return s;
} }

View File

@ -6,6 +6,7 @@ import nl.andrewl.railsignalapi.dao.ComponentRepository;
import nl.andrewl.railsignalapi.dao.RailSystemRepository; import nl.andrewl.railsignalapi.dao.RailSystemRepository;
import nl.andrewl.railsignalapi.dao.SegmentRepository; import nl.andrewl.railsignalapi.dao.SegmentRepository;
import nl.andrewl.railsignalapi.live.ComponentDownlinkService; import nl.andrewl.railsignalapi.live.ComponentDownlinkService;
import nl.andrewl.railsignalapi.live.dto.ErrorMessage;
import nl.andrewl.railsignalapi.live.dto.SegmentBoundaryUpdateMessage; import nl.andrewl.railsignalapi.live.dto.SegmentBoundaryUpdateMessage;
import nl.andrewl.railsignalapi.live.dto.SegmentStatusMessage; import nl.andrewl.railsignalapi.live.dto.SegmentStatusMessage;
import nl.andrewl.railsignalapi.live.websocket.AppUpdateService; import nl.andrewl.railsignalapi.live.websocket.AppUpdateService;
@ -83,8 +84,21 @@ public class SegmentService {
public void onBoundaryUpdate(SegmentBoundaryUpdateMessage msg) { public void onBoundaryUpdate(SegmentBoundaryUpdateMessage msg) {
var segmentBoundary = segmentBoundaryRepository.findById(msg.cId) var segmentBoundary = segmentBoundaryRepository.findById(msg.cId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
switch (msg.eventType) { segmentRepository.findByIdAndRailSystemId(msg.toSegmentId, segmentBoundary.getRailSystem().getId())
.ifPresentOrElse(
segment -> handleSegmentBoundaryMessage(msg.eventType, segmentBoundary, segment),
() -> downlinkService.sendMessage(new ErrorMessage(msg.cId, "Invalid toSegmentId."))
);
}
private void handleSegmentBoundaryMessage(
SegmentBoundaryUpdateMessage.Type type,
SegmentBoundaryNode segmentBoundary,
Segment toSegment
) {
switch (type) {
case ENTERING -> { case ENTERING -> {
log.info("Train entering segment {} in rail system {}.", toSegment.getName(), segmentBoundary.getRailSystem().getName());
for (var segment : segmentBoundary.getSegments()) { for (var segment : segmentBoundary.getSegments()) {
if (!segment.isOccupied()) { if (!segment.isOccupied()) {
segment.setOccupied(true); segment.setOccupied(true);
@ -94,20 +108,17 @@ public class SegmentService {
} }
} }
case ENTERED -> { case ENTERED -> {
log.info("Train has entered segment {} in rail system {}.", toSegment.getName(), segmentBoundary.getRailSystem().getName());
List<Segment> otherSegments = new ArrayList<>(segmentBoundary.getSegments()); List<Segment> otherSegments = new ArrayList<>(segmentBoundary.getSegments());
// Set the "to" segment as occupied. // Set the "to" segment as occupied.
segmentRepository.findById(msg.toSegmentId).ifPresent(segment -> { toSegment.setOccupied(true);
segment.setOccupied(true); segmentRepository.save(toSegment);
segmentRepository.save(segment); otherSegments.remove(toSegment);
sendSegmentOccupiedStatus(segment);
otherSegments.remove(segment);
});
// And all others as no longer occupied. // And all others as no longer occupied.
for (var segment : otherSegments) { for (var segment : otherSegments) {
if (segment.isOccupied()) { log.info("Train has left segment {} in rail system {}.", segment.getName(), segmentBoundary.getRailSystem().getName());
segment.setOccupied(false); segment.setOccupied(false);
segmentRepository.save(segment); segmentRepository.save(segment);
}
sendSegmentOccupiedStatus(segment); sendSegmentOccupiedStatus(segment);
} }
} }