Add input validation to SpiriLink
This commit is contained in:
parent
7a804b8ba6
commit
f74c3f6e1e
|
@ -32,26 +32,26 @@ enabled = true;
|
|||
// Runs the initPage when the document is loaded
|
||||
document.onload = InitPage();
|
||||
// Save file button
|
||||
document.getElementById("save").addEventListener("click", SaveSettings);
|
||||
document.getElementById("save").addEventListener("click", saveSettings);
|
||||
|
||||
// This attempts to read the conf file, if it exists, then it will parse it and fill out the table
|
||||
// if it fails then the values are loaded with defaults.
|
||||
function InitPage() {
|
||||
cockpit.script(scriptLocation + "cockpitScript.sh -v")
|
||||
.then((content) => version.innerHTML=content)
|
||||
.catch(error => Fail(error));
|
||||
.catch(error => displayFail(error));
|
||||
cockpit.script(scriptLocation + "cockpitScript.sh -u")
|
||||
.then(function(content) {
|
||||
ipsubnet1.innerHTML=content;
|
||||
ipsubnet2.innerHTML=content;
|
||||
})
|
||||
.catch(error => Fail(error));
|
||||
.catch(error => displayFail(error));
|
||||
|
||||
file_location.innerHTML = confLocation + "main.conf";
|
||||
|
||||
cockpit.file(confLocation + "main.conf")
|
||||
.read().then((content, tag) => SuccessReadFile(content))
|
||||
.catch(error => FailureReadFile(error));
|
||||
.catch(error => failureReadFile(error));
|
||||
}
|
||||
|
||||
function getValueByKey(text, sectionName, sectionLength, key) {
|
||||
|
@ -158,7 +158,7 @@ function SuccessReadFile(content) {
|
|||
var currentbaudRate = getValueByKey(content, "[UartEndpoint alpha]", 2, "Baud");
|
||||
cockpit.script(scriptLocation + "cockpitScript.sh -s")
|
||||
.then((content) => AddDropDown(fmuDevice, content.split("\n"), currentfmuDevice))
|
||||
.catch(error => Fail(error));
|
||||
.catch(error => displayFail(error));
|
||||
AddDropDown(baudrate, baudRateArray, currentbaudRate);
|
||||
|
||||
//UDP Telemetry
|
||||
|
@ -225,7 +225,7 @@ function SuccessReadFile(content) {
|
|||
}
|
||||
}
|
||||
catch(e) {
|
||||
FailureReadFile(e);
|
||||
failureReadFile(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,11 +249,11 @@ function AddDropDown(box, theArray, defaultValue) {
|
|||
}
|
||||
}
|
||||
catch(e) {
|
||||
Fail(e)
|
||||
displayFail(e)
|
||||
}
|
||||
}
|
||||
|
||||
function FailureReadFile(error) {
|
||||
function failureReadFile(error) {
|
||||
// Display error message
|
||||
console.log("Error : " + error.message);
|
||||
output.innerHTML = "Error : " + error.message;
|
||||
|
@ -267,7 +267,7 @@ function FailureReadFile(error) {
|
|||
}
|
||||
|
||||
|
||||
function SaveSettings() {
|
||||
function saveSettings() {
|
||||
|
||||
//lets do some validation
|
||||
|
||||
|
@ -306,7 +306,7 @@ function SaveSettings() {
|
|||
//open the file for writing, and callback function for modification
|
||||
cockpit.file(confLocation + "main.conf")
|
||||
.read().then((content, tag) => SuccessReadforSaveFile(content))
|
||||
.catch(error => FailureReadFile(error));
|
||||
.catch(error => failureReadFile(error));
|
||||
}
|
||||
|
||||
function SuccessReadforSaveFile(content) {
|
||||
|
@ -335,23 +335,23 @@ function SuccessReadforSaveFile(content) {
|
|||
content = setValueByKey(content, "[UartEndpoint alpha]",2, "Baud", baudrate.value )
|
||||
|
||||
cockpit.file(confLocation + "main.conf", { superuser : "try" }).replace(content)
|
||||
.then(Success)
|
||||
.catch(Fail);
|
||||
.then(displaySuccess)
|
||||
.catch(displayFail);
|
||||
|
||||
cockpit.spawn(["systemctl", "restart", "mavlink-router"], { superuser : "try" });
|
||||
}
|
||||
catch(e) {
|
||||
FailureReadFile(e);
|
||||
failureReadFile(e);
|
||||
}
|
||||
}
|
||||
|
||||
function Success() {
|
||||
function displaySuccess() {
|
||||
result.style.color = "green";
|
||||
result.innerHTML = "Success, mavlink-router restarting...";
|
||||
setTimeout(() => result.innerHTML = "", 5000);
|
||||
}
|
||||
|
||||
function Fail(error) {
|
||||
function displayFail(error) {
|
||||
result.style.color = "red";
|
||||
result.innerHTML = error.message;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<title>SpiriLink Configuration</title>
|
||||
<meta charset="utf-8">
|
||||
<link href="../base1/bootstrap.min.css" type="text/css" rel="stylesheet">
|
||||
<link href="../base1/style.css" type="text/css" rel="stylesheet">
|
||||
<!-- <link href="../base1/style.css" type="text/css" rel="stylesheet"> -->
|
||||
<script src="../base1/cockpit.js"></script>
|
||||
<script src="../base1/jquery-3.7.1.min.js"></script>
|
||||
</head>
|
||||
|
@ -20,8 +20,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<form class="row" id="spiriLinkForm" action="javascript:void(0);" novalidate>
|
||||
<!-- Save Button -->
|
||||
<div class="row mt-4 mb-4">
|
||||
<div class="row mt-2 mb-3">
|
||||
<div class="col-6">
|
||||
<button class="btn btn-primary" id="save">Save/Update</button>
|
||||
</div>
|
||||
|
@ -30,7 +31,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<pre id="result" class="mt-3 mb-3"></pre>
|
||||
<pre id="result" class="ms-1"></pre>
|
||||
|
||||
<!-- WiFi Configuration -->
|
||||
<div class="row mt-4">
|
||||
|
@ -54,6 +55,7 @@
|
|||
<label for="wifiTxPower" class="form-label">WiFi Tx Power (dBm * 100)</label>
|
||||
<input type="number" id="wifiTxPower" class="form-control"
|
||||
placeholder="e.g., 3000 for 30dBm">
|
||||
<div class="invalid-feedback">Must be an integer.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -72,11 +74,13 @@
|
|||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label for="tempInterval" class="form-label">Measurement Interval (s)</label>
|
||||
<input type="number" id="tempInterval" class="form-control" value="10">
|
||||
<input type="number" id="tempInterval" class="form-control" required>
|
||||
<div class="invalid-feedback">Must be an integer.</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="tempWarning" class="form-label">Overheat Warning (°C)</label>
|
||||
<input type="number" id="tempWarning" class="form-control" value="60">
|
||||
<input type="number" id="tempWarning" class="form-control" step="0.1" required>
|
||||
<div class="invalid-feedback">Must be a number with at most one decimal. </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -95,19 +99,22 @@
|
|||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<label for="droneVideoFwmark" class="form-label">FWMark</label>
|
||||
<input type="number" id="droneVideoFwmark" class="form-control">
|
||||
<input type="number" id="droneVideoFwmark" class="form-control" required>
|
||||
<div class="invalid-feedback">Must be an integer.</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="droneVideoType" class="form-label">Connection Type</label>
|
||||
<select id="droneVideoType" class="form-select"></select>
|
||||
<select id="droneVideoType" class="form-select" required></select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="droneVideoIP" class="form-label">IP Address</label>
|
||||
<input type="text" id="droneVideoIP" class="form-control">
|
||||
<input type="text" id="droneVideoIP" class="form-control" required>
|
||||
<div class="invalid-feedback">IP Address must be in the format xxx.xxx.xxx.xxx</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="droneVideoPort" class="form-label">Port</label>
|
||||
<input type="number" id="droneVideoPort" class="form-control">
|
||||
<input type="number" id="droneVideoPort" class="form-control" required>
|
||||
<div class="invalid-feedback">Port must be between 1 and 65535</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -126,19 +133,22 @@
|
|||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<label for="droneMavlinkFwmark" class="form-label">FWMark</label>
|
||||
<input type="number" id="droneMavlinkFwmark" class="form-control">
|
||||
<input type="number" id="droneMavlinkFwmark" class="form-control" required>
|
||||
<div class="invalid-feedback">Must be an integer.</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="droneMavlinkType" class="form-label">Connection Type</label>
|
||||
<select id="droneMavlinkType" class="form-select"></select>
|
||||
<select id="droneMavlinkType" class="form-select" required></select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="droneMavlinkIP" class="form-label">IP Address</label>
|
||||
<input type="text" id="droneMavlinkIP" class="form-control">
|
||||
<input type="text" id="droneMavlinkIP" class="form-control" required>
|
||||
<div class="invalid-feedback">IP Address must be in the format xxx.xxx.xxx.xxx</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="droneMavlinkPort" class="form-label">Port</label>
|
||||
<input type="number" id="droneMavlinkPort" class="form-control">
|
||||
<input type="number" id="droneMavlinkPort" class="form-control" required>
|
||||
<div class="invalid-feedback">Port must be between 1 and 65535</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -161,22 +171,26 @@
|
|||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="mavlinkSysId" class="form-label">System ID</label>
|
||||
<input type="number" id="mavlinkSysId" class="form-control" required>
|
||||
<input type="number" id="mavlinkSysId" class="form-control" min="1" max="255" required>
|
||||
<div class="invalid-feedback">System ID must be between 1 and 255</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="mavlinkCompId" class="form-label">Component ID</label>
|
||||
<input type="number" id="mavlinkCompId" class="form-control">
|
||||
<input type="number" id="mavlinkCompId" class="form-control" min="1" max="255" required>
|
||||
<div class="invalid-feedback">Comp ID must be between 1 and 255</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="mavlinkTcpPort" class="form-label">MAVLink TCP Port</label>
|
||||
<input type="number" id="mavlinkTcpPort" class="form-control"
|
||||
placeholder="None if left blank">
|
||||
<div class="invalid-feedback">Port must be between 1 and 65535</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="spirilink.js"></script>
|
||||
|
|
|
@ -23,6 +23,7 @@ const mavlinkSysId = document.getElementById("mavlinkSysId");
|
|||
const mavlinkCompId = document.getElementById("mavlinkCompId");
|
||||
const mavlinkTcpPort = document.getElementById("mavlinkTcpPort");
|
||||
|
||||
const spiriLinkForm = document.getElementById("spiriLinkForm");
|
||||
const output = document.getElementById("output");
|
||||
|
||||
const wifiChannels = [
|
||||
|
@ -74,7 +75,7 @@ function initPage() {
|
|||
|
||||
const inputs = document.querySelectorAll("input, select, textarea");
|
||||
inputs.forEach(input => {
|
||||
input.addEventListener("focus", clearResult);
|
||||
input.addEventListener("focus", resetForm);
|
||||
});
|
||||
|
||||
cockpit.file(confLocation)
|
||||
|
@ -85,6 +86,7 @@ function initPage() {
|
|||
// Load configuration values from the configuration file
|
||||
function successReadFile(content) {
|
||||
try {
|
||||
|
||||
// WiFi Configuration
|
||||
// Remove surrounding quotes from wifi_region value if present
|
||||
const currentWifiChannel = getValueByKey(content, "common", "wifi_channel");
|
||||
|
@ -131,14 +133,69 @@ function successReadFile(content) {
|
|||
}
|
||||
}
|
||||
|
||||
// Log error message and display it
|
||||
// TODO: Add more error handling for failed file reads
|
||||
function failureReadFile(error) {
|
||||
// Display error message
|
||||
console.log("Error : " + error.message);
|
||||
console.error("Error : " + error.message);
|
||||
displayFail(error.message)
|
||||
}
|
||||
|
||||
// Validate the form using non-standard input validation conditions.
|
||||
// Additional checks for IP, port, and system/component ID fields
|
||||
// Returns true if all fields are valid, false otherwise
|
||||
function validateSpiriLinkForm(form) {
|
||||
const ipformat = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||
const portformat = /^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;
|
||||
let isValid = true;
|
||||
|
||||
defaultValidation = form.checkValidity();
|
||||
|
||||
const inputs = form.querySelectorAll("input, select, textarea");
|
||||
inputs.forEach(input => {
|
||||
if (input.value === "") {
|
||||
isValid = !input.hasAttribute("required");
|
||||
setValidationVisuals(input, isValid);
|
||||
} else {
|
||||
if (input.id === "droneMavlinkIP" || input.id === "droneVideoIP") {
|
||||
isValid = input.value.match(ipformat);
|
||||
setValidationVisuals(input, isValid);
|
||||
}
|
||||
else if (input.id === "droneMavlinkPort" || input.id === "droneVideoPort" || input.id === "mavlinkTcpPort") {
|
||||
isValid = input.value.match(portformat);
|
||||
setValidationVisuals(input, isValid);
|
||||
}
|
||||
else if (input.id === "mavlinkSysId" || input.id === "mavlinkCompId") {
|
||||
isValid = input.value > 1 || input.value < 255;
|
||||
setValidationVisuals(input, isValid);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
form.classList.add("was-validated");
|
||||
if (!isValid || !defaultValidation) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set validation visuals for input fields
|
||||
// Adds or removes is-invalid class based on the validity of the input
|
||||
// The setCustomValidity function triggers the actual field visuals.
|
||||
// Giving it an empty string assumes the input is valid.
|
||||
function setValidationVisuals(input, isValid) {
|
||||
if (isValid) {
|
||||
input.classList.remove("is-invalid");
|
||||
input.setCustomValidity("");
|
||||
} else {
|
||||
input.classList.add("is-invalid");
|
||||
input.setCustomValidity("Invalid input");
|
||||
}
|
||||
}
|
||||
|
||||
// Save configuration values to the configuration file
|
||||
function saveSettings() {
|
||||
|
||||
const dataIsValid = validateSpiriLinkForm(spiriLinkForm);
|
||||
if (!dataIsValid) return;
|
||||
|
||||
try {
|
||||
cockpit.file(confLocation)
|
||||
.read()
|
||||
|
@ -231,9 +288,18 @@ function displayFail(error) {
|
|||
result.innerHTML = "Error : " + error;
|
||||
}
|
||||
|
||||
// Clear the result message
|
||||
function clearResult() {
|
||||
// Reset & clear form data
|
||||
function resetForm() {
|
||||
result.innerHTML = "";
|
||||
const inputs = spiriLinkForm.querySelectorAll("input, select, textarea");
|
||||
inputs.forEach(input => {
|
||||
input.setCustomValidity("");
|
||||
input.classList.remove("is-invalid");
|
||||
input.classList.remove("is-valid");
|
||||
});
|
||||
|
||||
spiriLinkForm.classList.remove("was-validated");
|
||||
spiriLinkForm.classList.add("needs-validation");
|
||||
}
|
||||
|
||||
function addDropDown(box, pairs, defaultValue) {
|
||||
|
@ -245,7 +311,6 @@ function addDropDown(box, pairs, defaultValue) {
|
|||
option.text = pairs[i][1];
|
||||
box.add(option);
|
||||
if (defaultValue === option.value) {
|
||||
console.log("found")
|
||||
box.value = option.value;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue