Added proper connection management.
This commit is contained in:
parent
3ac886feeb
commit
a02758ecd4
|
@ -2,7 +2,7 @@
|
||||||
<h2>{{railSystem.name}}</h2>
|
<h2>{{railSystem.name}}</h2>
|
||||||
<div>
|
<div>
|
||||||
<MapView :railSystem="railSystem" v-if="railSystem.segments && railSystem.components" />
|
<MapView :railSystem="railSystem" v-if="railSystem.segments && railSystem.components" />
|
||||||
<ComponentView v-if="railSystem.selectedComponent" :component="railSystem.selectedComponent"/>
|
<ComponentView v-if="railSystem.selectedComponent" :component="railSystem.selectedComponent" :railSystem="railSystem"/>
|
||||||
</div>
|
</div>
|
||||||
<SegmentsView />
|
<SegmentsView />
|
||||||
<AddSignal v-if="railSystem.segments && railSystem.segments.length > 0" />
|
<AddSignal v-if="railSystem.segments && railSystem.segments.length > 0" />
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
export function roundedRect(ctx, x, y, w, h, r) {
|
||||||
|
if (w < 2 * r) r = w / 2;
|
||||||
|
if (h < 2 * r) r = h / 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x+r, y);
|
||||||
|
ctx.arcTo(x+w, y, x+w, y+h, r);
|
||||||
|
ctx.arcTo(x+w, y+h, x, y+h, r);
|
||||||
|
ctx.arcTo(x, y+h, x, y, r);
|
||||||
|
ctx.arcTo(x, y, x+w, y, r);
|
||||||
|
ctx.closePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function circle(ctx, x, y, r) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, r, 0, Math.PI * 2);
|
||||||
|
}
|
|
@ -15,16 +15,16 @@
|
||||||
</p>
|
</p>
|
||||||
<SignalComponentView v-if="component.type === 'SIGNAL'" :signal="component" />
|
<SignalComponentView v-if="component.type === 'SIGNAL'" :signal="component" />
|
||||||
<SegmentBoundaryNodeComponentView v-if="component.type === 'SEGMENT_BOUNDARY'" :node="component" />
|
<SegmentBoundaryNodeComponentView v-if="component.type === 'SEGMENT_BOUNDARY'" :node="component" />
|
||||||
<PathNodeComponentView v-if="component.connectedNodes" :pathNode="component" />
|
<PathNodeComponentView v-if="component.connectedNodes" :pathNode="component" :railSystem="railSystem" />
|
||||||
<button @click="rsStore.removeComponent(component.id)">Remove</button>
|
<button @click="rsStore.removeComponent(component.id)">Remove</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {useRailSystemsStore} from "../../../stores/railSystemsStore";
|
|
||||||
import SignalComponentView from "./SignalComponentView.vue";
|
import SignalComponentView from "./SignalComponentView.vue";
|
||||||
import PathNodeComponentView from "./PathNodeComponentView.vue";
|
import PathNodeComponentView from "./PathNodeComponentView.vue";
|
||||||
import SegmentBoundaryNodeComponentView from "./SegmentBoundaryNodeComponentView.vue";
|
import SegmentBoundaryNodeComponentView from "./SegmentBoundaryNodeComponentView.vue";
|
||||||
|
import {useRailSystemsStore} from "../../../stores/railSystemsStore";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -42,6 +42,10 @@ export default {
|
||||||
component: {
|
component: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
railSystem: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,68 @@
|
||||||
<ul v-if="pathNode.connectedNodes.length > 0">
|
<ul v-if="pathNode.connectedNodes.length > 0">
|
||||||
<li v-for="node in pathNode.connectedNodes" :key="node.id">
|
<li v-for="node in pathNode.connectedNodes" :key="node.id">
|
||||||
{{node.id}} | {{node.name}}
|
{{node.id}} | {{node.name}}
|
||||||
|
<button @click="rsStore.removeConnection(pathNode, node)">Remove</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p v-if="pathNode.connectedNodes.length === 0">
|
<p v-if="pathNode.connectedNodes.length === 0">
|
||||||
There are no connected nodes.
|
There are no connected nodes.
|
||||||
</p>
|
</p>
|
||||||
|
<form @submit.prevent="rsStore.addConnection(pathNode, formData.nodeToAdd)">
|
||||||
|
<label for="pathNodeAddConnection">Add Connection</label>
|
||||||
|
<select id="pathNodeAddConnection" v-model="formData.nodeToAdd">
|
||||||
|
<option v-for="node in this.getEligibleConnections()" :key="node.id" :value="node">
|
||||||
|
{{node.id}} | {{node.name}} | {{node.type}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<button type="submit">Add</button>
|
||||||
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import {useRailSystemsStore} from "../../../stores/railSystemsStore";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "PathNodeComponentView",
|
name: "PathNodeComponentView",
|
||||||
|
setup() {
|
||||||
|
const rsStore = useRailSystemsStore();
|
||||||
|
return {
|
||||||
|
rsStore
|
||||||
|
};
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
pathNode: {
|
pathNode: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
railSystem: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
nodeToAdd: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getEligibleConnections() {
|
||||||
|
const nodes = [];
|
||||||
|
for (let i = 0; i < this.railSystem.components.length; i++) {
|
||||||
|
const c = this.railSystem.components[i];
|
||||||
|
if (c.id !== this.pathNode.id && c.connectedNodes !== undefined && c.connectedNodes !== null) {
|
||||||
|
let exists = false;
|
||||||
|
for (let j = 0; j < this.pathNode.connectedNodes.length; j++) {
|
||||||
|
if (this.pathNode.connectedNodes[j].id === c.id) {
|
||||||
|
exists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!exists) nodes.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
Helper functions to actually perform rendering of different components.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {getScaleFactor, isComponentHovered} from "./mapRenderer";
|
||||||
|
import {roundedRect, circle} from "./canvasUtils";
|
||||||
|
|
||||||
|
export function drawComponent(ctx, worldTx, component) {
|
||||||
|
const tx = DOMMatrix.fromMatrix(worldTx);
|
||||||
|
tx.translateSelf(component.position.x, component.position.z, 0);
|
||||||
|
const s = getScaleFactor();
|
||||||
|
tx.scaleSelf(1/s, 1/s, 1/s);
|
||||||
|
tx.scaleSelf(20, 20, 20);
|
||||||
|
|
||||||
|
ctx.setTransform(tx.translate(0.75, -0.75));
|
||||||
|
drawOnlineIndicator(ctx, component);
|
||||||
|
|
||||||
|
ctx.setTransform(tx);
|
||||||
|
|
||||||
|
// Draw hovered status.
|
||||||
|
if (isComponentHovered(component)) {
|
||||||
|
ctx.fillStyle = `rgba(255, 255, 0, 32)`;
|
||||||
|
circle(ctx, 0, 0, 0.75);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
if (component.type === "SIGNAL") {
|
||||||
|
drawSignal(ctx, component);
|
||||||
|
} else if (component.type === "SEGMENT_BOUNDARY") {
|
||||||
|
drawSegmentBoundary(ctx, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSignal(ctx, signal) {
|
||||||
|
roundedRect(ctx, -0.3, -0.5, 0.6, 1, 0.25);
|
||||||
|
ctx.fillStyle = "black";
|
||||||
|
ctx.fill();
|
||||||
|
ctx.fillStyle = "rgb(0, 255, 0)";
|
||||||
|
circle(ctx, 0, -0.2, 0.1);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSegmentBoundary(ctx, segmentBoundary) {
|
||||||
|
ctx.fillStyle = `rgb(150, 58, 224)`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, -0.5);
|
||||||
|
ctx.lineTo(-0.5, 0);
|
||||||
|
ctx.lineTo(0, 0.5);
|
||||||
|
ctx.lineTo(0.5, 0);
|
||||||
|
ctx.lineTo(0, -0.5);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawOnlineIndicator(ctx, component) {
|
||||||
|
ctx.lineWidth = 0.1;
|
||||||
|
if (component.online) {
|
||||||
|
ctx.fillStyle = `rgba(52, 174, 235, 128)`;
|
||||||
|
ctx.strokeStyle = `rgba(52, 174, 235, 128)`;
|
||||||
|
} else {
|
||||||
|
ctx.fillStyle = `rgba(153, 153, 153, 128)`;
|
||||||
|
ctx.strokeStyle = `rgba(153, 153, 153, 128)`;
|
||||||
|
}
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(0, 0.2, 0.125, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
for (let r = 0; r < 3; r++) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(0, 0, 0.1 + 0.2 * r, 7 * Math.PI / 6, 11 * Math.PI / 6);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function drawConnectedNodes(ctx, worldTx, component) {
|
||||||
|
// const tx = DOMMatrix.fromMatrix(worldTx);
|
||||||
|
const s = getScaleFactor();
|
||||||
|
// tx.scaleSelf(1/s, 1/s, 1/s);
|
||||||
|
// tx.scaleSelf(20, 20, 20);
|
||||||
|
// ctx.setTransform(tx);
|
||||||
|
ctx.lineWidth = 5 / s;
|
||||||
|
ctx.strokeStyle = "black";
|
||||||
|
for (let i = 0; i < component.connectedNodes.length; i++) {
|
||||||
|
const node = component.connectedNodes[i];
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(component.position.x, component.position.z);
|
||||||
|
ctx.lineTo(node.position.x, node.position.z);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,8 @@ This component is responsible for the rendering of a RailSystem in a 2d map
|
||||||
view.
|
view.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {drawComponent, drawConnectedNodes} from "./drawing";
|
||||||
|
|
||||||
const SCALE_VALUES = [0.01, 0.1, 0.25, 0.5, 1.0, 1.25, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 10.0, 12.0, 16.0, 20.0, 30.0, 45.0, 60.0, 80.0, 100.0];
|
const SCALE_VALUES = [0.01, 0.1, 0.25, 0.5, 1.0, 1.25, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 10.0, 12.0, 16.0, 20.0, 30.0, 45.0, 60.0, 80.0, 100.0];
|
||||||
const SCALE_INDEX_NORMAL = 7;
|
const SCALE_INDEX_NORMAL = 7;
|
||||||
const HOVER_RADIUS = 10;
|
const HOVER_RADIUS = 10;
|
||||||
|
@ -49,6 +51,13 @@ export function draw() {
|
||||||
const worldTx = getWorldTransform();
|
const worldTx = getWorldTransform();
|
||||||
ctx.setTransform(worldTx);
|
ctx.setTransform(worldTx);
|
||||||
|
|
||||||
|
for (let i = 0; i < railSystem.components.length; i++) {
|
||||||
|
const c = railSystem.components[i];
|
||||||
|
if (c.connectedNodes !== undefined && c.connectedNodes !== null) {
|
||||||
|
drawConnectedNodes(ctx, worldTx, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < railSystem.components.length; i++) {
|
for (let i = 0; i < railSystem.components.length; i++) {
|
||||||
drawComponent(ctx, worldTx, railSystem.components[i]);
|
drawComponent(ctx, worldTx, railSystem.components[i]);
|
||||||
}
|
}
|
||||||
|
@ -70,50 +79,6 @@ export function draw() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawComponent(ctx, worldTx, component) {
|
|
||||||
const tx = DOMMatrix.fromMatrix(worldTx);
|
|
||||||
tx.translateSelf(component.position.x, component.position.z, 0);
|
|
||||||
const s = getScaleFactor();
|
|
||||||
tx.scaleSelf(1/s, 1/s, 1/s);
|
|
||||||
tx.scaleSelf(5, 5, 5);
|
|
||||||
ctx.setTransform(tx);
|
|
||||||
if (isComponentHovered(component)) {
|
|
||||||
ctx.fillStyle = `rgba(255, 255, 0, 32)`;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.ellipse(0, 0, 1.8, 1.8, 0, 0, Math.PI * 2);
|
|
||||||
ctx.fill();
|
|
||||||
}
|
|
||||||
if (component.type === "SIGNAL") {
|
|
||||||
drawSignal(ctx, component);
|
|
||||||
} else if (component.type === "SEGMENT_BOUNDARY") {
|
|
||||||
drawSegmentBoundary(ctx, component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawSignal(ctx, signal) {
|
|
||||||
roundedRect(ctx, -0.7, -1, 1.4, 2, 0.25);
|
|
||||||
ctx.fillStyle = "black";
|
|
||||||
ctx.fill();
|
|
||||||
|
|
||||||
// ctx.fillStyle = "green";
|
|
||||||
// ctx.beginPath();
|
|
||||||
// ctx.ellipse(0, 0, 0.8, 0.8, 0, 0, Math.PI * 2);
|
|
||||||
// ctx.fill();
|
|
||||||
//
|
|
||||||
// ctx.strokeStyle = "black";
|
|
||||||
// ctx.lineWidth = 0.5;
|
|
||||||
// ctx.beginPath();
|
|
||||||
// ctx.ellipse(0, 0, 1, 1, 0, 0, Math.PI * 2);
|
|
||||||
// ctx.stroke();
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawSegmentBoundary(ctx, segmentBoundary) {
|
|
||||||
ctx.fillStyle = "blue";
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.ellipse(0, 0, 1, 1, 0, 0, Math.PI * 2);
|
|
||||||
ctx.fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getScaleFactor() {
|
export function getScaleFactor() {
|
||||||
return SCALE_VALUES[mapScaleIndex];
|
return SCALE_VALUES[mapScaleIndex];
|
||||||
}
|
}
|
||||||
|
@ -131,7 +96,7 @@ function getWorldTransform() {
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isComponentHovered(component) {
|
export function isComponentHovered(component) {
|
||||||
for (let i = 0; i < hoveredElements.length; i++) {
|
for (let i = 0; i < hoveredElements.length; i++) {
|
||||||
if (hoveredElements[i].id === component.id) return true;
|
if (hoveredElements[i].id === component.id) return true;
|
||||||
}
|
}
|
||||||
|
@ -156,18 +121,6 @@ function worldPointToMap(p) {
|
||||||
return getWorldTransform().transformPoint(p);
|
return getWorldTransform().transformPoint(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
function roundedRect(ctx, x, y, w, h, r) {
|
|
||||||
if (w < 2 * r) r = w / 2;
|
|
||||||
if (h < 2 * r) r = h / 2;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(x+r, y);
|
|
||||||
ctx.arcTo(x+w, y, x+w, y+h, r);
|
|
||||||
ctx.arcTo(x+w, y+h, x, y+h, r);
|
|
||||||
ctx.arcTo(x, y+h, x, y, r);
|
|
||||||
ctx.arcTo(x, y, x+w, y, r);
|
|
||||||
ctx.closePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
EVENT HANDLING
|
EVENT HANDLING
|
||||||
*/
|
*/
|
||||||
|
@ -184,6 +137,7 @@ function onMouseWheel(event) {
|
||||||
}
|
}
|
||||||
draw();
|
draw();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -54,10 +54,11 @@ export const useRailSystemsStore = defineStore('RailSystemsStore', {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
refreshComponents(rs) {
|
refreshAllComponents(rs) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
axios.get(`${this.apiUrl}/rs/${rs.id}/c`)
|
axios.get(`${this.apiUrl}/rs/${rs.id}/c`)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
rs.selectedComponent = null;
|
||||||
rs.components = response.data;
|
rs.components = response.data;
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
@ -66,7 +67,7 @@ export const useRailSystemsStore = defineStore('RailSystemsStore', {
|
||||||
fetchSelectedRailSystemData() {
|
fetchSelectedRailSystemData() {
|
||||||
if (!this.selectedRailSystem) return;
|
if (!this.selectedRailSystem) return;
|
||||||
this.refreshSegments(this.selectedRailSystem);
|
this.refreshSegments(this.selectedRailSystem);
|
||||||
this.refreshComponents(this.selectedRailSystem);
|
this.refreshAllComponents(this.selectedRailSystem);
|
||||||
},
|
},
|
||||||
addSegment(name) {
|
addSegment(name) {
|
||||||
const rs = this.selectedRailSystem;
|
const rs = this.selectedRailSystem;
|
||||||
|
@ -83,13 +84,13 @@ export const useRailSystemsStore = defineStore('RailSystemsStore', {
|
||||||
addComponent(data) {
|
addComponent(data) {
|
||||||
const rs = this.selectedRailSystem;
|
const rs = this.selectedRailSystem;
|
||||||
axios.post(`${this.apiUrl}/rs/${rs.id}/c`, data)
|
axios.post(`${this.apiUrl}/rs/${rs.id}/c`, data)
|
||||||
.then(() => this.refreshComponents(rs))
|
.then(() => this.refreshAllComponents(rs))
|
||||||
.catch(error => console.log(error));
|
.catch(error => console.log(error));
|
||||||
},
|
},
|
||||||
removeComponent(id) {
|
removeComponent(id) {
|
||||||
const rs = this.selectedRailSystem;
|
const rs = this.selectedRailSystem;
|
||||||
axios.delete(`${this.apiUrl}/rs/${rs.id}/c/${id}`)
|
axios.delete(`${this.apiUrl}/rs/${rs.id}/c/${id}`)
|
||||||
.then(() => this.refreshComponents(rs))
|
.then(() => this.refreshAllComponents(rs))
|
||||||
.catch(error => console.log(error));
|
.catch(error => console.log(error));
|
||||||
},
|
},
|
||||||
fetchComponentData(component) {
|
fetchComponentData(component) {
|
||||||
|
@ -99,6 +100,51 @@ export const useRailSystemsStore = defineStore('RailSystemsStore', {
|
||||||
.then(response => resolve(response.data))
|
.then(response => resolve(response.data))
|
||||||
.catch(error => console.log(error));
|
.catch(error => console.log(error));
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
refreshComponents(components) {
|
||||||
|
const rs = this.selectedRailSystem;
|
||||||
|
for (let i = 0; i < components.length; i++) {
|
||||||
|
axios.get(`${this.apiUrl}/rs/${rs.id}/c/${components[i].id}`)
|
||||||
|
.then(resp => {
|
||||||
|
const idx = this.selectedRailSystem.components.findIndex(c => c.id === resp.data.id);
|
||||||
|
if (idx > -1) this.selectedRailSystem.components[idx] = resp.data;
|
||||||
|
})
|
||||||
|
.catch(error => console.log(error));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateConnections(pathNode) {
|
||||||
|
const rs = this.selectedRailSystem;
|
||||||
|
return new Promise(resolve => {
|
||||||
|
axios.patch(
|
||||||
|
`${this.apiUrl}/rs/${rs.id}/c/${pathNode.id}/connectedNodes`,
|
||||||
|
pathNode
|
||||||
|
)
|
||||||
|
.then(response => {
|
||||||
|
pathNode.connectedNodes = response.data.connectedNodes;
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch(error => console.log(error));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addConnection(pathNode, other) {
|
||||||
|
pathNode.connectedNodes.push(other);
|
||||||
|
this.updateConnections(pathNode)
|
||||||
|
.then(() => {
|
||||||
|
this.refreshComponents(pathNode.connectedNodes);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeConnection(pathNode, other) {
|
||||||
|
const idx = pathNode.connectedNodes.findIndex(n => n.id === other.id);
|
||||||
|
if (idx > -1) {
|
||||||
|
pathNode.connectedNodes.splice(idx, 1);
|
||||||
|
this.updateConnections(pathNode)
|
||||||
|
.then(() => {
|
||||||
|
const nodes = [];
|
||||||
|
nodes.push(pathNode.connectedNodes);
|
||||||
|
nodes.push(other);
|
||||||
|
this.refreshComponents(nodes);
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -5,10 +5,7 @@ import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import nl.andrewl.railsignalapi.model.RailSystem;
|
import nl.andrewl.railsignalapi.model.RailSystem;
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.*;
|
||||||
import javax.persistence.Inheritance;
|
|
||||||
import javax.persistence.InheritanceType;
|
|
||||||
import javax.persistence.ManyToMany;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +20,7 @@ public abstract class PathNode extends Component {
|
||||||
/**
|
/**
|
||||||
* The set of nodes that this one is connected to.
|
* The set of nodes that this one is connected to.
|
||||||
*/
|
*/
|
||||||
@ManyToMany
|
@ManyToMany(cascade = CascadeType.DETACH)
|
||||||
private Set<PathNode> connectedNodes;
|
private Set<PathNode> connectedNodes;
|
||||||
|
|
||||||
public PathNode(RailSystem railSystem, Position position, String name, ComponentType type, Set<PathNode> connectedNodes) {
|
public PathNode(RailSystem railSystem, Position position, String name, ComponentType type, Set<PathNode> connectedNodes) {
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
package nl.andrewl.railsignalapi.rest.dto;
|
package nl.andrewl.railsignalapi.rest.dto;
|
||||||
|
|
||||||
public record PathNodeUpdatePayload (
|
import java.util.List;
|
||||||
long[] connectedNodeIds
|
|
||||||
) {}
|
public class PathNodeUpdatePayload {
|
||||||
|
public List<NodeIdObj> connectedNodes;
|
||||||
|
public static class NodeIdObj {
|
||||||
|
public long id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -128,7 +128,8 @@ public class ComponentService {
|
||||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
if (!(c instanceof PathNode p)) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Component is not a PathNode.");
|
if (!(c instanceof PathNode p)) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Component is not a PathNode.");
|
||||||
Set<PathNode> newNodes = new HashSet<>();
|
Set<PathNode> newNodes = new HashSet<>();
|
||||||
for (var id : payload.connectedNodeIds()) {
|
for (var nodeObj : payload.connectedNodes) {
|
||||||
|
long id = nodeObj.id;
|
||||||
var c1 = componentRepository.findByIdAndRailSystemId(id, rsId);
|
var c1 = componentRepository.findByIdAndRailSystemId(id, rsId);
|
||||||
if (c1.isPresent() && c1.get() instanceof PathNode pn) {
|
if (c1.isPresent() && c1.get() instanceof PathNode pn) {
|
||||||
newNodes.add(pn);
|
newNodes.add(pn);
|
||||||
|
@ -136,8 +137,23 @@ public class ComponentService {
|
||||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Component with id " + id + " is not a PathNode in the same rail system.");
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Component with id " + id + " is not a PathNode in the same rail system.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.getConnectedNodes().retainAll(newNodes);
|
|
||||||
p.getConnectedNodes().addAll(newNodes);
|
Set<PathNode> nodesToRemove = new HashSet<>(p.getConnectedNodes());
|
||||||
|
nodesToRemove.removeAll(newNodes);
|
||||||
|
|
||||||
|
Set<PathNode> nodesToAdd = new HashSet<>(newNodes);
|
||||||
|
nodesToAdd.removeAll(p.getConnectedNodes());
|
||||||
|
|
||||||
|
p.getConnectedNodes().removeAll(nodesToRemove);
|
||||||
|
p.getConnectedNodes().addAll(nodesToAdd);
|
||||||
|
for (var node : nodesToRemove) {
|
||||||
|
node.getConnectedNodes().remove(p);
|
||||||
|
}
|
||||||
|
for (var node : nodesToAdd) {
|
||||||
|
node.getConnectedNodes().add(p);
|
||||||
|
}
|
||||||
|
componentRepository.saveAll(nodesToRemove);
|
||||||
|
componentRepository.saveAll(nodesToAdd);
|
||||||
p = componentRepository.save(p);
|
p = componentRepository.save(p);
|
||||||
return ComponentResponse.of(p);
|
return ComponentResponse.of(p);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue