diff --git a/pom.xml b/pom.xml
index 0a08909..231d602 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
nl.andrewl
rail-signal-api
- 2.1.0
+ 2.2.0
rail-signal-api
A simple API for tracking rail traffic in signalled blocks.
diff --git a/quasar-app/src/components/rs/component_views/BaseComponentView.vue b/quasar-app/src/components/rs/component_views/BaseComponentView.vue
index cf878d7..a0c44ff 100644
--- a/quasar-app/src/components/rs/component_views/BaseComponentView.vue
+++ b/quasar-app/src/components/rs/component_views/BaseComponentView.vue
@@ -70,7 +70,7 @@ export default {
message: "Are you sure you want to remove this component? This cannot be undone.",
cancel: true
}).onOk(() => {
- removeComponent(this.railSystem, component.id)
+ removeComponent(this.rsStore.selectedRailSystem, component.id)
.then(() => {
this.quasar.notify({
color: "positive",
diff --git a/quasar-app/src/components/rs/component_views/SwitchComponentView.vue b/quasar-app/src/components/rs/component_views/SwitchComponentView.vue
index 25013f2..8667c42 100644
--- a/quasar-app/src/components/rs/component_views/SwitchComponentView.vue
+++ b/quasar-app/src/components/rs/component_views/SwitchComponentView.vue
@@ -127,7 +127,19 @@ export default {
},
methods: {
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) {
return this.sw.activeConfiguration !== null && this.sw.activeConfiguration.id === config.id;
diff --git a/quasar-app/src/render/canvasUtils.js b/quasar-app/src/render/canvasUtils.js
index fbad3a5..f0c9d0e 100644
--- a/quasar-app/src/render/canvasUtils.js
+++ b/quasar-app/src/render/canvasUtils.js
@@ -23,3 +23,28 @@ export function mulberry32(a) {
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;
+}
diff --git a/quasar-app/src/render/drawing.js b/quasar-app/src/render/drawing.js
index c722f20..db6119c 100644
--- a/quasar-app/src/render/drawing.js
+++ b/quasar-app/src/render/drawing.js
@@ -3,7 +3,7 @@ Helper functions to actually perform rendering of different components.
*/
import { getScaleFactor, getWorldTransform, isComponentHovered, isComponentSelected } from "./mapRenderer";
-import { circle, roundedRect } from "./canvasUtils";
+import { circle, roundedRect, sortPoints } from "./canvasUtils";
import randomColor from "randomcolor";
export function drawMap(ctx, rs) {
@@ -16,6 +16,7 @@ export function drawMap(ctx, rs) {
function drawSegments(ctx, rs) {
const segmentPoints = new Map();
+ // Gather for each segment a set of points representing its bounds.
rs.segments.forEach(segment => segmentPoints.set(segment.id, []));
for (let i = 0; i < rs.components.length; 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++) {
const color = randomColor({ luminosity: 'light', format: 'rgb', seed: rs.segments[i].id });
diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/SwitchPayload.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/SwitchPayload.java
index 5e4fc43..38d9f5c 100644
--- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/SwitchPayload.java
+++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/SwitchPayload.java
@@ -1,14 +1,15 @@
package nl.andrewl.railsignalapi.rest.dto.component.in;
import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class SwitchPayload extends ComponentPayload {
- @NotEmpty @Size(min = 2, max = 10)
+ @NotNull @Size(max = 10)
public SwitchConfigurationPayload[] possibleConfigurations;
public static class SwitchConfigurationPayload {
- @NotEmpty @Size(min = 2, max = 10)
+ @NotEmpty @Size(min = 2, max = 2)
public NodePayload[] nodes;
public static class NodePayload {
diff --git a/src/main/java/nl/andrewl/railsignalapi/service/ComponentCreationService.java b/src/main/java/nl/andrewl/railsignalapi/service/ComponentCreationService.java
index 31a518e..baa6a47 100644
--- a/src/main/java/nl/andrewl/railsignalapi/service/ComponentCreationService.java
+++ b/src/main/java/nl/andrewl/railsignalapi/service/ComponentCreationService.java
@@ -87,9 +87,6 @@ public class ComponentCreationService {
}
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;
}
diff --git a/src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java b/src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java
index 5218095..47be561 100644
--- a/src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java
+++ b/src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java
@@ -6,6 +6,7 @@ import nl.andrewl.railsignalapi.dao.ComponentRepository;
import nl.andrewl.railsignalapi.dao.RailSystemRepository;
import nl.andrewl.railsignalapi.dao.SegmentRepository;
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.SegmentStatusMessage;
import nl.andrewl.railsignalapi.live.websocket.AppUpdateService;
@@ -83,8 +84,21 @@ public class SegmentService {
public void onBoundaryUpdate(SegmentBoundaryUpdateMessage msg) {
var segmentBoundary = segmentBoundaryRepository.findById(msg.cId)
.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 -> {
+ log.info("Train entering segment {} in rail system {}.", toSegment.getName(), segmentBoundary.getRailSystem().getName());
for (var segment : segmentBoundary.getSegments()) {
if (!segment.isOccupied()) {
segment.setOccupied(true);
@@ -94,20 +108,17 @@ public class SegmentService {
}
}
case ENTERED -> {
+ log.info("Train has entered segment {} in rail system {}.", toSegment.getName(), segmentBoundary.getRailSystem().getName());
List otherSegments = new ArrayList<>(segmentBoundary.getSegments());
// Set the "to" segment as occupied.
- segmentRepository.findById(msg.toSegmentId).ifPresent(segment -> {
- segment.setOccupied(true);
- segmentRepository.save(segment);
- sendSegmentOccupiedStatus(segment);
- otherSegments.remove(segment);
- });
+ toSegment.setOccupied(true);
+ segmentRepository.save(toSegment);
+ otherSegments.remove(toSegment);
// And all others as no longer occupied.
for (var segment : otherSegments) {
- if (segment.isOccupied()) {
- segment.setOccupied(false);
- segmentRepository.save(segment);
- }
+ log.info("Train has left segment {} in rail system {}.", segment.getName(), segmentBoundary.getRailSystem().getName());
+ segment.setOccupied(false);
+ segmentRepository.save(segment);
sendSegmentOccupiedStatus(segment);
}
}