added stuff
This commit is contained in:
parent
e608e2ba8c
commit
3ac886feeb
|
@ -9,6 +9,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
|
"bootstrap": "^4.6.1",
|
||||||
"pinia": "^2.0.14",
|
"pinia": "^2.0.14",
|
||||||
"three": "^0.140.0",
|
"three": "^0.140.0",
|
||||||
"vue": "^3.2.33",
|
"vue": "^3.2.33",
|
||||||
|
@ -285,6 +286,19 @@
|
||||||
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
|
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/bootstrap": {
|
||||||
|
"version": "4.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz",
|
||||||
|
"integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/bootstrap"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"jquery": "1.9.1 - 3",
|
||||||
|
"popper.js": "^1.16.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
@ -1248,6 +1262,12 @@
|
||||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/jquery": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
|
@ -1500,6 +1520,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/popper.js": {
|
||||||
|
"version": "1.16.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
||||||
|
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
|
||||||
|
"deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
|
||||||
|
"peer": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.13",
|
"version": "8.4.13",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz",
|
||||||
|
@ -2130,6 +2161,12 @@
|
||||||
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
|
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"bootstrap": {
|
||||||
|
"version": "4.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz",
|
||||||
|
"integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
@ -2744,6 +2781,12 @@
|
||||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"jquery": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"js-yaml": {
|
"js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
|
@ -2924,6 +2967,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"popper.js": {
|
||||||
|
"version": "1.16.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
|
||||||
|
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"postcss": {
|
"postcss": {
|
||||||
"version": "8.4.13",
|
"version": "8.4.13",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz",
|
||||||
|
|
|
@ -4,10 +4,7 @@
|
||||||
</header>
|
</header>
|
||||||
<RailSystemsManager />
|
<RailSystemsManager />
|
||||||
|
|
||||||
<RailSystem
|
<RailSystem v-if="rsStore.selectedRailSystem !== null" :railSystem="rsStore.selectedRailSystem"/>
|
||||||
v-if="rsStore.selectedRailSystem !== null"
|
|
||||||
:railSystem="rsStore.selectedRailSystem"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -1,29 +1,35 @@
|
||||||
<template>
|
<template>
|
||||||
<h2>{{railSystem.name}}</h2>
|
<h2>{{railSystem.name}}</h2>
|
||||||
<RsMap :railSystem="railSystem" />
|
<div>
|
||||||
<RsComponent v-if="selectedComponent !== null" :component="selectedComponent" />
|
<MapView :railSystem="railSystem" v-if="railSystem.segments && railSystem.components" />
|
||||||
|
<ComponentView v-if="railSystem.selectedComponent" :component="railSystem.selectedComponent"/>
|
||||||
|
</div>
|
||||||
|
<SegmentsView />
|
||||||
|
<AddSignal v-if="railSystem.segments && railSystem.segments.length > 0" />
|
||||||
|
<AddSegmentBoundary v-if="railSystem.segments && railSystem.segments.length > 0" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import RsMap from './railsystem/MapView.vue'
|
import MapView from './railsystem/MapView.vue'
|
||||||
import RsComponent from './railsystem/Component.vue'
|
import ComponentView from './railsystem/component/ComponentView.vue'
|
||||||
|
import SegmentsView from "./railsystem/SegmentsView.vue";
|
||||||
|
import AddSignal from "./railsystem/component/AddSignal.vue";
|
||||||
|
import AddSegmentBoundary from "./railsystem/component/AddSegmentBoundary.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
RsMap,
|
AddSignal,
|
||||||
RsComponent
|
AddSegmentBoundary,
|
||||||
},
|
SegmentsView,
|
||||||
props: {
|
MapView,
|
||||||
railSystem: {
|
ComponentView
|
||||||
type: Object,
|
},
|
||||||
required: true
|
props: {
|
||||||
}
|
railSystem: {
|
||||||
},
|
type: Object,
|
||||||
data() {
|
required: true
|
||||||
return {
|
|
||||||
selectedComponent: null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,12 @@ export default {
|
||||||
name: "RailSystemsManager.vue",
|
name: "RailSystemsManager.vue",
|
||||||
setup() {
|
setup() {
|
||||||
const rsStore = useRailSystemsStore();
|
const rsStore = useRailSystemsStore();
|
||||||
|
rsStore.$subscribe(mutation => {
|
||||||
|
const evt = mutation.events;
|
||||||
|
if (evt.key === "selectedRailSystem" && evt.newValue !== null) {
|
||||||
|
rsStore.fetchSelectedRailSystemData();
|
||||||
|
}
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
rsStore
|
rsStore
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,14 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<form>
|
<h4>Add Segment</h4>
|
||||||
|
<form @submit.prevent="rsStore.addSegment(this.formData.segmentName)">
|
||||||
<label for="addSegmentName">Name</label>
|
<label for="addSegmentName">Name</label>
|
||||||
<input type="text" v-model="formData.segmentName" />
|
<input type="text" v-model="formData.segmentName" />
|
||||||
<button>Add</button>
|
<button type="submit">Add</button>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import {useRailSystemsStore} from "../../stores/railSystemsStore";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "AddSegment",
|
name: "AddSegment",
|
||||||
|
setup() {
|
||||||
|
const rsStore = useRailSystemsStore();
|
||||||
|
return {
|
||||||
|
rsStore
|
||||||
|
};
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
formData: {
|
formData: {
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
component: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="rs-component">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.rs-component {
|
|
||||||
width: 20%;
|
|
||||||
border: 1px solid black;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,26 +1,32 @@
|
||||||
<script>
|
<script>
|
||||||
|
import {initMap} from "./mapRenderer.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
railSystem: {
|
railSystem: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// The first time this map is mounted, initialize the map.
|
||||||
|
initMap(this.railSystem);
|
||||||
|
},
|
||||||
|
updated() {
|
||||||
|
// Also, re-initialize any time this view is updated.
|
||||||
|
initMap(this.railSystem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<canvas>
|
<canvas id="railSystemMapCanvas" width="1000" height="600">
|
||||||
Your browser doesn't support canvas!
|
Your browser doesn't support canvas!
|
||||||
</canvas>
|
</canvas>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
canvas {
|
canvas {
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
width: 70%;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -0,0 +1,31 @@
|
||||||
|
<template>
|
||||||
|
<h3>Segments</h3>
|
||||||
|
<ul>
|
||||||
|
<li v-for="segment in rsStore.selectedRailSystem.segments" :key="segment.id">
|
||||||
|
{{segment.name}} <button @click.prevent="rsStore.removeSegment(segment.id)">Remove</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<AddSegment />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AddSegment from "./AddSegment.vue";
|
||||||
|
import {useRailSystemsStore} from "../../stores/railSystemsStore";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "SegmentsView.vue",
|
||||||
|
components: {
|
||||||
|
AddSegment
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const rsStore = useRailSystemsStore();
|
||||||
|
return {
|
||||||
|
rsStore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,76 @@
|
||||||
|
<template>
|
||||||
|
<h4>Add Segment Boundary</h4>
|
||||||
|
<form @submit.prevent="submit()">
|
||||||
|
<div>
|
||||||
|
<label for="addSBX">X</label>
|
||||||
|
<input type="number" id="addSBX" v-model="formData.position.x" required/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="addSBY">Y</label>
|
||||||
|
<input type="number" id="addSBY" v-model="formData.position.y" required/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="addSBZ">Z</label>
|
||||||
|
<input type="number" id="addSBZ" v-model="formData.position.z" required/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="addSBName">Name</label>
|
||||||
|
<input type="text" id="addSBName" v-model="formData.name"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="addSBSegmentA">Segment A</label>
|
||||||
|
<select id="addSBSegmentA" v-model="formData.segmentA">
|
||||||
|
<option v-for="segment in rsStore.selectedRailSystem.segments" :key="segment.id" :value="segment">
|
||||||
|
{{segment.id}} | {{segment.name}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<label for="addSBSegmentB">Segment B</label>
|
||||||
|
<select id="addSBSegmentB" v-model="formData.segmentB">
|
||||||
|
<option v-for="segment in rsStore.selectedRailSystem.segments" :key="segment.id" :value="segment">
|
||||||
|
{{segment.id}} | {{segment.name}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {useRailSystemsStore} from "../../../stores/railSystemsStore";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "AddSegmentBoundary.vue",
|
||||||
|
setup() {
|
||||||
|
const rsStore = useRailSystemsStore();
|
||||||
|
return {
|
||||||
|
rsStore
|
||||||
|
};
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
name: "",
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
segmentA: null,
|
||||||
|
segmentB: null,
|
||||||
|
segments: [],
|
||||||
|
type: "SEGMENT_BOUNDARY"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
this.formData.segments = [this.formData.segmentA, this.formData.segmentB];
|
||||||
|
this.rsStore.addComponent(this.formData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,62 @@
|
||||||
|
<template>
|
||||||
|
<h4>Add Signal</h4>
|
||||||
|
<form @submit.prevent="rsStore.addComponent(formData)">
|
||||||
|
<div>
|
||||||
|
<label for="addSignalX">X</label>
|
||||||
|
<input type="number" id="addSignalX" v-model="formData.position.x" required/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="addSignalY">Y</label>
|
||||||
|
<input type="number" id="addSignalY" v-model="formData.position.y" required/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="addSignalZ">Z</label>
|
||||||
|
<input type="number" id="addSignalZ" v-model="formData.position.z" required/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="addSignalName">Name</label>
|
||||||
|
<input type="text" id="addSignalName" v-model="formData.name"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="addSignalSegment">Segment</label>
|
||||||
|
<select id="addSignalSegment" v-model="formData.segment">
|
||||||
|
<option v-for="segment in rsStore.selectedRailSystem.segments" :key="segment.id" :value="segment">
|
||||||
|
{{segment.id}} | {{segment.name}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {useRailSystemsStore} from "../../../stores/railSystemsStore";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "AddSignal",
|
||||||
|
setup() {
|
||||||
|
const rsStore = useRailSystemsStore();
|
||||||
|
return {
|
||||||
|
rsStore
|
||||||
|
};
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
name: "",
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
},
|
||||||
|
segment: null,
|
||||||
|
type: "SIGNAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,55 @@
|
||||||
|
<template>
|
||||||
|
<div class="rs-component">
|
||||||
|
<h3>{{component.name}}</h3>
|
||||||
|
<p>
|
||||||
|
Id: {{component.id}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Position: (x = {{component.position.x}}, y = {{component.position.y}}, z = {{component.position.z}})
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Type: {{component.type}}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Online: {{component.online}}
|
||||||
|
</p>
|
||||||
|
<SignalComponentView v-if="component.type === 'SIGNAL'" :signal="component" />
|
||||||
|
<SegmentBoundaryNodeComponentView v-if="component.type === 'SEGMENT_BOUNDARY'" :node="component" />
|
||||||
|
<PathNodeComponentView v-if="component.connectedNodes" :pathNode="component" />
|
||||||
|
<button @click="rsStore.removeComponent(component.id)">Remove</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {useRailSystemsStore} from "../../../stores/railSystemsStore";
|
||||||
|
import SignalComponentView from "./SignalComponentView.vue";
|
||||||
|
import PathNodeComponentView from "./PathNodeComponentView.vue";
|
||||||
|
import SegmentBoundaryNodeComponentView from "./SegmentBoundaryNodeComponentView.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
SegmentBoundaryNodeComponentView,
|
||||||
|
SignalComponentView,
|
||||||
|
PathNodeComponentView
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const rsStore = useRailSystemsStore();
|
||||||
|
return {
|
||||||
|
rsStore
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
component: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.rs-component {
|
||||||
|
width: 20%;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,27 @@
|
||||||
|
<template>
|
||||||
|
<h5>Connected Nodes</h5>
|
||||||
|
<ul v-if="pathNode.connectedNodes.length > 0">
|
||||||
|
<li v-for="node in pathNode.connectedNodes" :key="node.id">
|
||||||
|
{{node.id}} | {{node.name}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p v-if="pathNode.connectedNodes.length === 0">
|
||||||
|
There are no connected nodes.
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "PathNodeComponentView",
|
||||||
|
props: {
|
||||||
|
pathNode: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<template>
|
||||||
|
<h5>Segments</h5>
|
||||||
|
<ul>
|
||||||
|
<li v-for="segment in node.segments" :key="segment.id">
|
||||||
|
{{segment.id}} | {{segment.name}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "SegmentBoundaryNodeComponentView",
|
||||||
|
props: {
|
||||||
|
node: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<p>
|
||||||
|
Connected segment: {{signal.segment.id}} {{signal.segment.name}}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "SignalComponentView",
|
||||||
|
props: {
|
||||||
|
signal: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,248 @@
|
||||||
|
/*
|
||||||
|
This component is responsible for the rendering of a RailSystem in a 2d map
|
||||||
|
view.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 HOVER_RADIUS = 10;
|
||||||
|
|
||||||
|
let mapCanvas = null;
|
||||||
|
let railSystem = null;
|
||||||
|
|
||||||
|
let mapScaleIndex = SCALE_INDEX_NORMAL;
|
||||||
|
let mapTranslation = {x: 0, y: 0};
|
||||||
|
let mapDragOrigin = null;
|
||||||
|
let mapDragTranslation = null;
|
||||||
|
let lastMousePoint = new DOMPoint(0, 0, 0, 0);
|
||||||
|
const hoveredElements = [];
|
||||||
|
|
||||||
|
export function initMap(rs) {
|
||||||
|
railSystem = rs;
|
||||||
|
console.log("Initializing map for rail system: " + rs.name);
|
||||||
|
hoveredElements.length = 0;
|
||||||
|
mapCanvas = document.getElementById("railSystemMapCanvas");
|
||||||
|
mapCanvas.removeEventListener("wheel", onMouseWheel);
|
||||||
|
mapCanvas.addEventListener("wheel", onMouseWheel);
|
||||||
|
mapCanvas.removeEventListener("mousedown", onMouseDown);
|
||||||
|
mapCanvas.addEventListener("mousedown", onMouseDown);
|
||||||
|
mapCanvas.removeEventListener("mouseup", onMouseUp);
|
||||||
|
mapCanvas.addEventListener("mouseup", onMouseUp);
|
||||||
|
mapCanvas.removeEventListener("mousemove", onMouseMove);
|
||||||
|
mapCanvas.addEventListener("mousemove", onMouseMove);
|
||||||
|
|
||||||
|
// Do an initial draw.
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function draw() {
|
||||||
|
if (!(mapCanvas && railSystem && railSystem.components)) {
|
||||||
|
console.warn("Attempted to draw map without canvas or railSystem.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ctx = mapCanvas.getContext("2d");
|
||||||
|
const width = mapCanvas.width;
|
||||||
|
const height = mapCanvas.height;
|
||||||
|
ctx.resetTransform();
|
||||||
|
ctx.fillStyle = `rgb(240, 240, 240)`;
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
const worldTx = getWorldTransform();
|
||||||
|
ctx.setTransform(worldTx);
|
||||||
|
|
||||||
|
for (let i = 0; i < railSystem.components.length; i++) {
|
||||||
|
drawComponent(ctx, worldTx, railSystem.components[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw debug info.
|
||||||
|
ctx.resetTransform();
|
||||||
|
ctx.fillStyle = "black";
|
||||||
|
ctx.strokeStyle = "black";
|
||||||
|
ctx.font = "10px Sans-Serif";
|
||||||
|
const lastWorldPoint = mapPointToWorld(lastMousePoint);
|
||||||
|
const lines = [
|
||||||
|
"Scale factor: " + getScaleFactor(),
|
||||||
|
`(x = ${lastWorldPoint.x.toFixed(2)}, y = ${lastWorldPoint.y.toFixed(2)}, z = ${lastWorldPoint.z.toFixed(2)})`,
|
||||||
|
`Components: ${railSystem.components.length}`,
|
||||||
|
`Hovered elements: ${hoveredElements.length}`
|
||||||
|
]
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
ctx.fillText(lines[i], 10, 20 + (i * 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
return SCALE_VALUES[mapScaleIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWorldTransform() {
|
||||||
|
const canvasRect = mapCanvas.getBoundingClientRect();
|
||||||
|
const scale = getScaleFactor();
|
||||||
|
const tx = new DOMMatrix();
|
||||||
|
tx.translateSelf(canvasRect.width / 2, canvasRect.height / 2, 0);
|
||||||
|
tx.scaleSelf(scale, scale, scale);
|
||||||
|
tx.translateSelf(mapTranslation.x, mapTranslation.y, 0);
|
||||||
|
if (mapDragOrigin !== null && mapDragTranslation !== null) {
|
||||||
|
tx.translateSelf(mapDragTranslation.x, mapDragTranslation.y, 0);
|
||||||
|
}
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isComponentHovered(component) {
|
||||||
|
for (let i = 0; i < hoveredElements.length; i++) {
|
||||||
|
if (hoveredElements[i].id === component.id) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a point on the map coordinates to world coordinates.
|
||||||
|
* @param {DOMPoint} p
|
||||||
|
* @returns {DOMPoint}
|
||||||
|
*/
|
||||||
|
function mapPointToWorld(p) {
|
||||||
|
return getWorldTransform().invertSelf().transformPoint(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a point in the world to map coordinates.
|
||||||
|
* @param {DOMPoint} p
|
||||||
|
* @returns {DOMPoint}
|
||||||
|
*/
|
||||||
|
function worldPointToMap(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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {WheelEvent} event
|
||||||
|
*/
|
||||||
|
function onMouseWheel(event) {
|
||||||
|
const s = event.deltaY;
|
||||||
|
if (s > 0) {
|
||||||
|
mapScaleIndex = Math.max(0, mapScaleIndex - 1);
|
||||||
|
} else if (s < 0) {
|
||||||
|
mapScaleIndex = Math.min(SCALE_VALUES.length - 1, mapScaleIndex + 1);
|
||||||
|
}
|
||||||
|
draw();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {MouseEvent} event
|
||||||
|
*/
|
||||||
|
function onMouseDown(event) {
|
||||||
|
const p = getMousePoint(event);
|
||||||
|
mapDragOrigin = {x: p.x, y: p.y};
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseUp() {
|
||||||
|
if (mapDragTranslation !== null) {
|
||||||
|
mapTranslation.x += mapDragTranslation.x;
|
||||||
|
mapTranslation.y += mapDragTranslation.y;
|
||||||
|
}
|
||||||
|
if (hoveredElements.length === 1) {
|
||||||
|
railSystem.selectedComponent = hoveredElements[0];
|
||||||
|
} else {
|
||||||
|
railSystem.selectedComponent = null;
|
||||||
|
}
|
||||||
|
mapDragOrigin = null;
|
||||||
|
mapDragTranslation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {MouseEvent} event
|
||||||
|
*/
|
||||||
|
function onMouseMove(event) {
|
||||||
|
const p = getMousePoint(event);
|
||||||
|
lastMousePoint = p;
|
||||||
|
if (mapDragOrigin !== null) {
|
||||||
|
const scale = getScaleFactor();
|
||||||
|
const dx = p.x - mapDragOrigin.x;
|
||||||
|
const dy = p.y - mapDragOrigin.y;
|
||||||
|
mapDragTranslation = {x: dx / scale, y: dy / scale};
|
||||||
|
} else {
|
||||||
|
hoveredElements.length = 0;
|
||||||
|
// Populate with list of hovered elements.
|
||||||
|
for (let i = 0; i < railSystem.components.length; i++) {
|
||||||
|
const c = railSystem.components[i];
|
||||||
|
const componentPoint = new DOMPoint(c.position.x, c.position.z, 0, 1);
|
||||||
|
const mapComponentPoint = worldPointToMap(componentPoint);
|
||||||
|
const dist2 = (p.x - mapComponentPoint.x) * (p.x - mapComponentPoint.x) + (p.y - mapComponentPoint.y) * (p.y - mapComponentPoint.y);
|
||||||
|
if (dist2 < HOVER_RADIUS * HOVER_RADIUS) {
|
||||||
|
hoveredElements.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the point at which the user clicked on the map.
|
||||||
|
* @param {MouseEvent} event
|
||||||
|
* @returns {DOMPoint}
|
||||||
|
*/
|
||||||
|
function getMousePoint(event) {
|
||||||
|
const rect = mapCanvas.getBoundingClientRect();
|
||||||
|
const x = event.clientX - rect.left;
|
||||||
|
const y = event.clientY - rect.top;
|
||||||
|
return new DOMPoint(x, y, 0, 1);
|
||||||
|
}
|
|
@ -4,13 +4,16 @@ import axios from "axios";
|
||||||
export const useRailSystemsStore = defineStore('RailSystemsStore', {
|
export const useRailSystemsStore = defineStore('RailSystemsStore', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
railSystems: [],
|
railSystems: [],
|
||||||
|
/**
|
||||||
|
* @type {{segments: [Object], components: [Object], selectedComponent: Object} | null}
|
||||||
|
*/
|
||||||
selectedRailSystem: null,
|
selectedRailSystem: null,
|
||||||
selectedComponent: null
|
apiUrl: import.meta.env.VITE_API_URL
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
refreshRailSystems() {
|
refreshRailSystems() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios.get(import.meta.env.VITE_API_URL + "/rs")
|
axios.get(this.apiUrl + "/rs")
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.railSystems = response.data;
|
this.railSystems = response.data;
|
||||||
resolve();
|
resolve();
|
||||||
|
@ -22,10 +25,7 @@ export const useRailSystemsStore = defineStore('RailSystemsStore', {
|
||||||
},
|
},
|
||||||
createRailSystem(name) {
|
createRailSystem(name) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios.post(
|
axios.post(this.apiUrl + "/rs", {name: name})
|
||||||
import.meta.env.VITE_API_URL + "/rs",
|
|
||||||
{name: name}
|
|
||||||
)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.refreshRailSystems()
|
this.refreshRailSystems()
|
||||||
.then(() => resolve())
|
.then(() => resolve())
|
||||||
|
@ -36,13 +36,69 @@ export const useRailSystemsStore = defineStore('RailSystemsStore', {
|
||||||
},
|
},
|
||||||
removeRailSystem(rs) {
|
removeRailSystem(rs) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios.delete(import.meta.env.VITE_API_URL + "/rs/" + rs.id)
|
axios.delete(this.apiUrl + "/rs/" + rs.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
this.selectedRailSystem = null;
|
||||||
this.refreshRailSystems()
|
this.refreshRailSystems()
|
||||||
.then(() => resolve)
|
.then(() => resolve)
|
||||||
.catch(error => reject(error));
|
.catch(error => reject(error));
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
refreshSegments(rs) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
axios.get(`${this.apiUrl}/rs/${rs.id}/s`)
|
||||||
|
.then(response => {
|
||||||
|
rs.segments = response.data;
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
refreshComponents(rs) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
axios.get(`${this.apiUrl}/rs/${rs.id}/c`)
|
||||||
|
.then(response => {
|
||||||
|
rs.components = response.data;
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchSelectedRailSystemData() {
|
||||||
|
if (!this.selectedRailSystem) return;
|
||||||
|
this.refreshSegments(this.selectedRailSystem);
|
||||||
|
this.refreshComponents(this.selectedRailSystem);
|
||||||
|
},
|
||||||
|
addSegment(name) {
|
||||||
|
const rs = this.selectedRailSystem;
|
||||||
|
axios.post(`${this.apiUrl}/rs/${rs.id}/s`, {name: name})
|
||||||
|
.then(() => this.refreshSegments(rs))
|
||||||
|
.catch(error => console.log(error));
|
||||||
|
},
|
||||||
|
removeSegment(id) {
|
||||||
|
const rs = this.selectedRailSystem;
|
||||||
|
axios.delete(`${this.apiUrl}/rs/${rs.id}/s/${id}`)
|
||||||
|
.then(() => this.refreshSegments(rs))
|
||||||
|
.catch(error => console.log(error));
|
||||||
|
},
|
||||||
|
addComponent(data) {
|
||||||
|
const rs = this.selectedRailSystem;
|
||||||
|
axios.post(`${this.apiUrl}/rs/${rs.id}/c`, data)
|
||||||
|
.then(() => this.refreshComponents(rs))
|
||||||
|
.catch(error => console.log(error));
|
||||||
|
},
|
||||||
|
removeComponent(id) {
|
||||||
|
const rs = this.selectedRailSystem;
|
||||||
|
axios.delete(`${this.apiUrl}/rs/${rs.id}/c/${id}`)
|
||||||
|
.then(() => this.refreshComponents(rs))
|
||||||
|
.catch(error => console.log(error));
|
||||||
|
},
|
||||||
|
fetchComponentData(component) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const rs = this.selectedRailSystem;
|
||||||
|
axios.get(`${this.apiUrl}/rs/${rs.id}/c/${component.id}`)
|
||||||
|
.then(response => resolve(response.data))
|
||||||
|
.catch(error => console.log(error));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -2,8 +2,8 @@ package nl.andrewl.railsignalapi.rest;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.component.ComponentResponse;
|
import nl.andrewl.railsignalapi.rest.dto.PathNodeUpdatePayload;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.component.SimpleComponentResponse;
|
import nl.andrewl.railsignalapi.rest.dto.component.out.ComponentResponse;
|
||||||
import nl.andrewl.railsignalapi.service.ComponentService;
|
import nl.andrewl.railsignalapi.service.ComponentService;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
@ -17,7 +17,7 @@ public class ComponentsApiController {
|
||||||
private final ComponentService componentService;
|
private final ComponentService componentService;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public List<SimpleComponentResponse> getAllComponents(@PathVariable long rsId) {
|
public List<ComponentResponse> getAllComponents(@PathVariable long rsId) {
|
||||||
return componentService.getComponents(rsId);
|
return componentService.getComponents(rsId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,4 +36,9 @@ public class ComponentsApiController {
|
||||||
componentService.removeComponent(rsId, cId);
|
componentService.removeComponent(rsId, cId);
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PatchMapping(path = "/{cId}/connectedNodes")
|
||||||
|
public ComponentResponse updateConnectedNodes(@PathVariable long rsId, @PathVariable long cId, @RequestBody PathNodeUpdatePayload payload) {
|
||||||
|
return componentService.updatePath(rsId, cId, payload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,10 @@ package nl.andrewl.railsignalapi.rest;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.FullSegmentResponse;
|
import nl.andrewl.railsignalapi.rest.dto.FullSegmentResponse;
|
||||||
|
import nl.andrewl.railsignalapi.rest.dto.SegmentPayload;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.SegmentResponse;
|
import nl.andrewl.railsignalapi.rest.dto.SegmentResponse;
|
||||||
import nl.andrewl.railsignalapi.service.SegmentService;
|
import nl.andrewl.railsignalapi.service.SegmentService;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -28,4 +30,10 @@ public class SegmentsApiController {
|
||||||
public FullSegmentResponse createSegment(@PathVariable long rsId, @RequestBody SegmentPayload payload) {
|
public FullSegmentResponse createSegment(@PathVariable long rsId, @RequestBody SegmentPayload payload) {
|
||||||
return segmentService.create(rsId, payload);
|
return segmentService.create(rsId, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteMapping(path = "/{sId}")
|
||||||
|
public ResponseEntity<Void> removeSegment(@PathVariable long rsId, @PathVariable long sId) {
|
||||||
|
segmentService.remove(rsId, sId);
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package nl.andrewl.railsignalapi.rest.dto;
|
package nl.andrewl.railsignalapi.rest.dto;
|
||||||
|
|
||||||
import nl.andrewl.railsignalapi.model.Segment;
|
import nl.andrewl.railsignalapi.model.Segment;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.component.SegmentBoundaryNodeResponse;
|
import nl.andrewl.railsignalapi.rest.dto.component.out.SegmentBoundaryNodeResponse;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.component.SignalResponse;
|
import nl.andrewl.railsignalapi.rest.dto.component.out.SignalResponse;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package nl.andrewl.railsignalapi.rest.dto;
|
||||||
|
|
||||||
|
public record PathNodeUpdatePayload (
|
||||||
|
long[] connectedNodeIds
|
||||||
|
) {}
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewl.railsignalapi.rest;
|
package nl.andrewl.railsignalapi.rest.dto;
|
||||||
|
|
||||||
public record SegmentPayload(String name) {
|
public record SegmentPayload(String name) {
|
||||||
}
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package nl.andrewl.railsignalapi.rest.dto.component.in;
|
||||||
|
|
||||||
|
import nl.andrewl.railsignalapi.model.component.Position;
|
||||||
|
|
||||||
|
public abstract class ComponentPayload {
|
||||||
|
public String name;
|
||||||
|
public String type;
|
||||||
|
public Position position;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package nl.andrewl.railsignalapi.rest.dto.component.in;
|
||||||
|
|
||||||
|
public class SignalPayload extends ComponentPayload {
|
||||||
|
public long segmentId;
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewl.railsignalapi.rest.dto.component;
|
package nl.andrewl.railsignalapi.rest.dto.component.out;
|
||||||
|
|
||||||
import nl.andrewl.railsignalapi.model.component.*;
|
import nl.andrewl.railsignalapi.model.component.*;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewl.railsignalapi.rest.dto.component;
|
package nl.andrewl.railsignalapi.rest.dto.component.out;
|
||||||
|
|
||||||
import nl.andrewl.railsignalapi.model.component.PathNode;
|
import nl.andrewl.railsignalapi.model.component.PathNode;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewl.railsignalapi.rest.dto.component;
|
package nl.andrewl.railsignalapi.rest.dto.component.out;
|
||||||
|
|
||||||
import nl.andrewl.railsignalapi.model.component.SegmentBoundaryNode;
|
import nl.andrewl.railsignalapi.model.component.SegmentBoundaryNode;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.SegmentResponse;
|
import nl.andrewl.railsignalapi.rest.dto.SegmentResponse;
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewl.railsignalapi.rest.dto.component;
|
package nl.andrewl.railsignalapi.rest.dto.component.out;
|
||||||
|
|
||||||
import nl.andrewl.railsignalapi.model.component.Signal;
|
import nl.andrewl.railsignalapi.model.component.Signal;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.SegmentResponse;
|
import nl.andrewl.railsignalapi.rest.dto.SegmentResponse;
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewl.railsignalapi.rest.dto.component;
|
package nl.andrewl.railsignalapi.rest.dto.component.out;
|
||||||
|
|
||||||
import nl.andrewl.railsignalapi.model.component.Component;
|
import nl.andrewl.railsignalapi.model.component.Component;
|
||||||
import nl.andrewl.railsignalapi.model.component.Position;
|
import nl.andrewl.railsignalapi.model.component.Position;
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewl.railsignalapi.rest.dto.component;
|
package nl.andrewl.railsignalapi.rest.dto.component.out;
|
||||||
|
|
||||||
import nl.andrewl.railsignalapi.model.component.SwitchConfiguration;
|
import nl.andrewl.railsignalapi.model.component.SwitchConfiguration;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewl.railsignalapi.rest.dto.component;
|
package nl.andrewl.railsignalapi.rest.dto.component.out;
|
||||||
|
|
||||||
import nl.andrewl.railsignalapi.model.component.Switch;
|
import nl.andrewl.railsignalapi.model.component.Switch;
|
||||||
|
|
|
@ -11,8 +11,8 @@ import nl.andrewl.railsignalapi.dao.SwitchConfigurationRepository;
|
||||||
import nl.andrewl.railsignalapi.model.RailSystem;
|
import nl.andrewl.railsignalapi.model.RailSystem;
|
||||||
import nl.andrewl.railsignalapi.model.Segment;
|
import nl.andrewl.railsignalapi.model.Segment;
|
||||||
import nl.andrewl.railsignalapi.model.component.*;
|
import nl.andrewl.railsignalapi.model.component.*;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.component.ComponentResponse;
|
import nl.andrewl.railsignalapi.rest.dto.PathNodeUpdatePayload;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.component.SimpleComponentResponse;
|
import nl.andrewl.railsignalapi.rest.dto.component.out.ComponentResponse;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
@ -31,10 +31,10 @@ public class ComponentService {
|
||||||
private final SwitchConfigurationRepository switchConfigurationRepository;
|
private final SwitchConfigurationRepository switchConfigurationRepository;
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<SimpleComponentResponse> getComponents(long rsId) {
|
public List<ComponentResponse> getComponents(long rsId) {
|
||||||
var rs = railSystemRepository.findById(rsId)
|
var rs = railSystemRepository.findById(rsId)
|
||||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
return componentRepository.findAllByRailSystem(rs).stream().map(SimpleComponentResponse::new).toList();
|
return componentRepository.findAllByRailSystem(rs).stream().map(ComponentResponse::of).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
|
@ -123,7 +123,22 @@ public class ComponentService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void remove(long rsId, long componentId) {
|
public ComponentResponse updatePath(long rsId, long cId, PathNodeUpdatePayload payload) {
|
||||||
|
var c = componentRepository.findByIdAndRailSystemId(cId, rsId)
|
||||||
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
|
if (!(c instanceof PathNode p)) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Component is not a PathNode.");
|
||||||
|
Set<PathNode> newNodes = new HashSet<>();
|
||||||
|
for (var id : payload.connectedNodeIds()) {
|
||||||
|
var c1 = componentRepository.findByIdAndRailSystemId(id, rsId);
|
||||||
|
if (c1.isPresent() && c1.get() instanceof PathNode pn) {
|
||||||
|
newNodes.add(pn);
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
p = componentRepository.save(p);
|
||||||
|
return ComponentResponse.of(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import nl.andrewl.railsignalapi.dao.RailSystemRepository;
|
||||||
import nl.andrewl.railsignalapi.dao.SegmentRepository;
|
import nl.andrewl.railsignalapi.dao.SegmentRepository;
|
||||||
import nl.andrewl.railsignalapi.model.Segment;
|
import nl.andrewl.railsignalapi.model.Segment;
|
||||||
import nl.andrewl.railsignalapi.model.component.Component;
|
import nl.andrewl.railsignalapi.model.component.Component;
|
||||||
import nl.andrewl.railsignalapi.rest.SegmentPayload;
|
import nl.andrewl.railsignalapi.rest.dto.SegmentPayload;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.FullSegmentResponse;
|
import nl.andrewl.railsignalapi.rest.dto.FullSegmentResponse;
|
||||||
import nl.andrewl.railsignalapi.rest.dto.SegmentResponse;
|
import nl.andrewl.railsignalapi.rest.dto.SegmentResponse;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
@ -53,5 +53,6 @@ public class SegmentService {
|
||||||
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
componentRepository.deleteAll(segment.getSignals());
|
componentRepository.deleteAll(segment.getSignals());
|
||||||
componentRepository.deleteAll(segment.getBoundaryNodes());
|
componentRepository.deleteAll(segment.getBoundaryNodes());
|
||||||
|
segmentRepository.delete(segment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue