Added functionality to add and remove connections to other signals.

This commit is contained in:
Andrew Lalis 2021-11-25 11:23:23 +01:00
parent 25e59cd92c
commit fd2cf357dd
3 changed files with 317 additions and 16 deletions

View File

@ -65,7 +65,7 @@ public class SignalService {
branch = this.branchRepository.findByNameAndRailSystem(branchData.name(), rs)
.orElse(new Branch(rs, branchData.name(), BranchStatus.FREE));
}
Direction dir = Direction.valueOf(branchData.direction().trim().toUpperCase());
Direction dir = Direction.parse(branchData.direction());
branchConnections.add(new SignalBranchConnection(signal, branch, dir));
}
signal = signalRepository.save(signal);

View File

@ -132,10 +132,34 @@ function railSystemChanged() {
canvasTranslation.y = -1 * (bb.y + (bb.height / 2));
canvasScaleIndex = SCALE_INDEX_NORMAL;
drawRailSystem();
window.setInterval(railSystemUpdated, 1000);
});
$.get("/api/railSystems/" + railSystem.id + "/branches")
.done(branches => {
railSystem.branches = branches;
const branchSelects = $('.js_branch_list');
branchSelects.empty();
railSystem.branches.forEach(branch => {
branchSelects.append($('<option value="' + branch.name + '"></option>'))
});
});
}
function railSystemUpdated() {
console.log("Refreshing...");
$.get("/api/railSystems/" + railSystem.id + "/signals")
.done(signals => {
railSystem.signals = signals;
drawRailSystem();
});
$.get("/api/railSystems/" + railSystem.id + "/branches")
.done(branches => {
railSystem.branches = branches;
const branchSelects = $('.js_branch_list');
branchSelects.empty();
railSystem.branches.forEach(branch => {
branchSelects.append($('<option value="' + branch.name + '"></option>'))
});
});
}
@ -152,6 +176,29 @@ function onSignalSelected(signal) {
if (signal !== null) {
const tpl = Handlebars.compile($('#signalTemplate').html());
detailPanel.html(tpl(signal));
signal.branchConnections.forEach(con => {
const select = $('#signalPotentialConnectionsSelect-' + con.id);
$.get("/api/railSystems/" + railSystem.id + "/branches/" + con.branch.id + "/signals")
.done(signals => {
signals = signals.filter(s => s.id !== signal.id);
let connections = [];
signals.forEach(s => {
s.branchConnections
.filter(c => c.branch.id === con.branch.id && !con.reachableSignalConnections.some(rc => rc.connectionId === c.id))
.forEach(potentialConnection => {
potentialConnection.signalName = s.name;
potentialConnection.signalId = s.id;
connections.push(potentialConnection);
});
});
select.empty();
const row = $('#signalPotentialConnectionsRow-' + con.id);
row.toggle(connections.length > 0);
connections.forEach(c => {
select.append($(`<option value="${c.id}">${c.signalName} via ${c.direction} connection ${c.id}</option>`))
});
});
});
}
}
@ -172,12 +219,15 @@ function addRailSystem() {
function deleteRailSystem() {
if (railSystem !== null && railSystem.id) {
$.ajax({
url: "/api/railSystems/" + railSystem.id,
type: "DELETE"
})
.always(() => {
refreshRailSystems(true);
confirm("Are you sure you want to permanently remove rail system " + railSystem.id + "?")
.then(() => {
$.ajax({
url: "/api/railSystems/" + railSystem.id,
type: "DELETE"
})
.always(() => {
refreshRailSystems(true);
});
});
}
}
@ -196,3 +246,134 @@ function refreshRailSystems(selectFirst) {
}
});
}
function addNewSignal() {
const modalElement = $('#addSignalModal');
const form = $('#addSignalForm');
form.validate();
if (!form.valid()) return;
const data = {
name: $('#addSignalName').val().trim(),
position: {
x: $('#addSignalPositionX').val(),
y: $('#addSignalPositionY').val(),
z: $('#addSignalPositionZ').val()
},
branchConnections: [
{
direction: $('#addSignalFirstConnectionDirection').val(),
name: $('#addSignalFirstConnectionBranch').val()
},
{
direction: $('#addSignalSecondConnectionDirection').val(),
name: $('#addSignalSecondConnectionBranch').val()
}
]
};
const modal = bootstrap.Modal.getInstance(modalElement[0]);
modal.hide();
modalElement.on("hidden.bs.modal", () => {
confirm("Are you sure you want to add this new signal to the system?")
.then(() => {
$.post({
url: "/api/railSystems/" + railSystem.id + "/signals",
data: JSON.stringify(data),
contentType: "application/json"
})
.done((response) => {
form.trigger("reset");
railSystemChanged();
})
.fail((response) => {
$('#addSignalAlertsContainer').append($('<div class="alert alert-danger">An error occurred.</div>'));
modal.show();
});
})
.catch(() => {
form.trigger("reset");
});
});
}
function removeReachableConnection(signalId, fromId, toId) {
confirm(`Are you sure you want to remove the connection from ${fromId} to ${toId} from signal ${signalId}?`)
.then(() => {
$.get(`/api/railSystems/${railSystem.id}/signals/${signalId}`)
.done(signal => {
let connections = [];
signal.branchConnections.forEach(con => {
con.reachableSignalConnections.forEach(reachableCon => {
connections.push({from: con.id, to: reachableCon.connectionId});
});
});
connections = connections.filter(c => !(c.from === fromId && c.to === toId));
$.post({
url: `/api/railSystems/${railSystem.id}/signals/${signal.id}/signalConnections`,
data: JSON.stringify({connections: connections}),
contentType: "application/json"
})
.done((response) => {
railSystemChanged();
})
.fail((response) => {
console.error(response);
});
});
});
}
function addReachableConnectionBtn(signalId, fromId) {
const select = $('#signalPotentialConnectionsSelect-' + fromId);
const toId = select.val();
if (toId) {
addReachableConnection(signalId, fromId, toId);
}
}
function addReachableConnection(signalId, fromId, toId) {
confirm(`Are you sure you want to add a connection from ${fromId} to ${toId} from signal ${signalId}?`)
.then(() => {
$.get(`/api/railSystems/${railSystem.id}/signals/${signalId}`)
.done(signal => {
let connections = [];
signal.branchConnections.forEach(con => {
con.reachableSignalConnections.forEach(reachableCon => {
connections.push({from: con.id, to: reachableCon.connectionId});
});
});
if (!connections.find(c => c.from === fromId && c.to === toId)) {
connections.push({from: fromId, to: toId});
$.post({
url: `/api/railSystems/${railSystem.id}/signals/${signal.id}/signalConnections`,
data: JSON.stringify({connections: connections}),
contentType: "application/json"
})
.done((response) => {
railSystemChanged();
})
.fail((response) => {
console.error(response);
});
}
});
});
}
function confirm(message) {
const modalElement = $('#confirmModal');
if (message) {
$('#confirmModalBody').html(message);
} else {
$('#confirmModalBody').html("Are you sure you want to continue?");
}
const modal = new bootstrap.Modal(modalElement[0], {keyboard: false});
modal.show();
return new Promise((resolve, reject) => {
$('#confirmModalOkButton').click(() => {
modalElement.on("hidden.bs.modal", () => resolve());
});
$('#confirmModalCancelButton').click(() => {
modalElement.on("hidden.bs.modal", () => reject());
});
});
}

View File

@ -43,9 +43,17 @@
<div id="railMapDetailPanel" class="col p-2 border">
</div>
</div>
<hr class="my-4"/>
<div class="row">
<div class="col">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addSignalModal">Add Signal</button>
</div>
</div>
</div>
<div style="display: none;">
<div>
<script id="signalTemplate" type="text/x-handlebars-template">
<h3>{{name}}</h3>
<hr>
@ -65,20 +73,132 @@
<dl class="row">
<dt class="col-sm-3">Branch</dt>
<dd class="col-sm-9">{{this.branch.name}} <small class="text-muted">{{this.branch.id}}</small></dd>
{{#if this.reachableSignalConnections.length}}
<dt class="col-sm-3">Reachable Signals</dt>
<dd class="col-sm-9">
{{#each this.reachableSignalConnections}}
<p><a onclick="selectSignalById({{this.signalId}})" href="#">{{this.signalName}}</a> <small class="text-muted">{{this.signalId}}</small> via {{this.direction}} connection {{this.connectionId}}</p>
{{/each}}
</dd>
{{/if}}
<dt class="col-sm-3">Reachable Signals</dt>
<dd class="col-sm-9">
{{#each this.reachableSignalConnections}}
<p>
<a onclick="selectSignalById({{this.signalId}})" href="#">{{this.signalName}}</a>
<small class="text-muted">{{this.signalId}}</small> via
{{this.direction}} connection {{this.connectionId}}
<span
class="btn btn-sm btn-danger js_removeSignalConnection"
onclick="removeReachableConnection({{../../id}}, {{../id}}, {{this.connectionId}})"
>Remove</span>
</p>
{{/each}}
</dd>
</dl>
<div class="row" id="signalPotentialConnectionsRow-{{this.id}}">
<div class="col-auto">
<select id="signalPotentialConnectionsSelect-{{this.id}}" class="form-select form-select-sm">
{{#each this.potentialSignalConnections}}
<option value="{{this.connectionId}}">{{this.signalName}} via {{this.branchName}} on {{this.direction}} connection</option>
{{/each}}
</select>
</div>
<div class="col-auto">
<button type="button" class="btn btn-primary btn-sm" onclick="addReachableConnectionBtn({{../id}}, {{this.id}})">Add Connection</button>
</div>
</div>
{{/each}}
</script>
<div class="modal fade" tabindex="-1" id="confirmModal" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 id="confirmModalHeader">Confirm</h5>
</div>
<div class="modal-body">
<p id="confirmModalBody">Are you sure you want to continue?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="confirmModalCancelButton">Cancel</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" id="confirmModalOkButton">Ok</button>
</div>
</div>
</div>
</div>
<div class="modal fade" tabindex="-1" id="addSignalModal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Signal</h5>
</div>
<div class="modal-body">
<p>Fill in the following data to add a new signal to the system.</p>
<form id="addSignalForm">
<div class="row mb-3">
<div class="col">
<input id="addSignalName" type="text" class="form-control" placeholder="Name" aria-label="Name" required>
</div>
</div>
<div class="row mb-3">
<h6>Position</h6>
<div class="col">
<input id="addSignalPositionX" class="form-control" placeholder="X" aria-label="X" type="number" step="0.1" required>
</div>
<div class="col">
<input id="addSignalPositionY" class="form-control" placeholder="Y" aria-label="Y" type="number" step="0.1" required>
</div>
<div class="col">
<input id="addSignalPositionZ" class="form-control" placeholder="Z" aria-label="Z" type="number" step="0.1" required>
</div>
</div>
<h6>Connections</h6>
<div class="row mb-3">
<div class="col">
<select id="addSignalFirstConnectionDirection" class="form-select" required>
<option value="N">North</option>
<option value="S">South</option>
<option value="E">East</option>
<option value="W">West</option>
<option value="NW">NorthWest</option>
<option value="NE">NorthEast</option>
<option value="SW">SouthWest</option>
<option value="SE">SouthEast</option>
</select>
</div>
<div class="col">
<input id="addSignalFirstConnectionBranch" type="text" class="form-control" placeholder="Branch Name" list="addSignalFirstConnectionBranchList" required>
<datalist id="addSignalFirstConnectionBranchList" class="js_branch_list">
</datalist>
</div>
</div>
<div class="row mb-3">
<div class="col">
<select id="addSignalSecondConnectionDirection" class="form-select" required>
<option value="N">North</option>
<option value="S">South</option>
<option value="E">East</option>
<option value="W">West</option>
<option value="NW">NorthWest</option>
<option value="NE">NorthEast</option>
<option value="SW">SouthWest</option>
<option value="SE">SouthEast</option>
</select>
</div>
<div class="col">
<input id="addSignalSecondConnectionBranch" type="text" class="form-control" placeholder="Branch Name" list="addSignalSecondConnectionBranchList" required>
<datalist id="addSignalSecondConnectionBranchList" class="js_branch_list">
</datalist>
</div>
</div>
</form>
<div id="addSignalAlertsContainer"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onclick="addNewSignal()">Save Changes</button>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-validation@1.19.3/dist/jquery.validate.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.7/handlebars.min.js" integrity="sha512-RNLkV3d+aLtfcpEyFG8jRbnWHxUqVZozacROI4J2F1sTaDqo1dPQYs01OMi1t1w9Y2FdbSCDSQ2ZVdAC8bzgAg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>