Version bump, and fixed some small issues with rendering and creating switches.
This commit is contained in:
parent
cb8eed835a
commit
99f438161f
2
pom.xml
2
pom.xml
|
@ -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>
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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 });
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue