Feat: Adding A configuration page

This commit is contained in:
Halcy0nic
2024-09-02 14:33:45 -06:00
parent aafca40df7
commit 33d9b565a9
4 changed files with 662 additions and 55 deletions
+1
View File
@@ -33,6 +33,7 @@
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/#config">Configuration</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#">Analysis Mode</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/survey">Survey Mode</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/tracking">Tracking Mode</a></li>
+659 -55
View File
@@ -19,6 +19,8 @@
<!-- Include SweetAlert2 JS -->
<script src="static/js/sweetalert2.js"></script>
<script src="static/js/jquery.min.js"></script>
</head>
@@ -33,6 +35,7 @@
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#about">Home</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#config">Configuration</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/analysis">Analysis Mode</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/survey">Survey Mode</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/tracking">Tracking Mode</a></li>
@@ -61,70 +64,671 @@
</div>
</section>
<!-- Add this form within the body where it fits best, possibly under the About section -->
<section class="resume-section">
<section class="resume-section" id="config">
<div class="resume-section-content">
<h2 class="mb-0" style="text-align: center;">LoRaWAN Gateway Configuration</h2><br>
<img src="static/assets/img/lorawan.webp" style="width:100%; "><br><br>
<div style="display: flex; justify-content: center; margin-top: 20px;"><button class="transmit-button" id="configureGatewayBtn">Configure Gateway</button></div>
<div class="config-section">
<h4>Configure Transceivers</h4>
<p>
The <strong>'Configure Transceiver'</strong> buttons allow you to attach to the serial port of an Adafruit Feather M0, enabling you to capture LoRa traffic on that frequency.
</p>
</div>
<!-- 433 -->
<div class="resume-section-content">
<div class="collapse" id="collapse433">
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">433 MHz LoRa Transceiver</h3><br>
<div class="serial-data" id="serial-data-port1">
</div>
<div style="text-align:center;">
<button class="styled-button" onclick="promptUser433()">Connect Serial Port</button>
<button class="disconnect-button" onclick="deleteSerial433()">Disconnect Serial Port</button>
<button class="transmit-button" onclick="transmit433()">Transmit Data</button><br><br>
<button class="transmit-button" onclick="clearDataPort1()">Clear Data</button>
<input type="checkbox" id="autoscroll-port1" checked>
<label for="autoscroll-port1">Autoscroll</label>
<p id="status-label433" style="text-align: center">Status: Checking...</p>
<div class="indicator-container">
<div id="indicator433" class="indicator"></div>
</div>
</div>
<script>
function clearDataPort1() {
var serialDataDiv1 = document.getElementById('serial-data-port1');
serialDataDiv1.innerHTML = '';
}
function clearDataPort2() {
var serialDataDiv2 = document.getElementById('serial-data-port2');
serialDataDiv2.innerHTML = '';
}
function clearDataPort3() {
var serialDataDiv3 = document.getElementById('serial-data-port3');
serialDataDiv3.innerHTML = '';
}
</script>
<!-- JavaScript for updating the indicator -->
<script>
function updateIndicator433() {
// Fetch the status from the Flask route
$.ajax({
url: '/checkSer',
type: 'GET',
data: {
port: 'port1',
},
success: function(response) {
if (response['result'] === 'True') {
$('#indicator433').removeClass('red').addClass('green');
$('#status-label433').text('Status: Connected');
} else {
$('#indicator433').removeClass('green').addClass('red');
$('#status-label433').text('Serial Port Status: Disconnected');
}
},
error: function() {
$('#indicator433').removeClass('green').addClass('red');
$('#status-label433').text('Serial Port Status: Disconnected');
}
});
}
// Initial update of the indicator
updateIndicator433();
// Periodically check and update the indicator (e.g., every 5 seconds)
setInterval(updateIndicator433, 5000);
</script>
<script type="text/javascript">
var socket = io.connect('http://' + document.domain + ':' + location.port);
// For port1
socket.on('initial_serial_data_port1', function(data) {
var serialDataDiv = document.getElementById('serial-data-port1');
data.data.forEach(function(line) {
serialDataDiv.innerHTML += line + '<br>';
});
// Scroll to the bottom for new data
//serialDataDiv.scrollTop = serialDataDiv.scrollHeight;
});
socket.on('serial_data_port1', function(data) {
var serialDataDiv = document.getElementById('serial-data-port1');
serialDataDiv.innerHTML += data.data + '<br>';
var autoscrollCheckbox = document.getElementById('autoscroll-port1');
if (autoscrollCheckbox.checked) {
serialDataDiv.scrollTop = serialDataDiv.scrollHeight;
}
});
</script>
<script>
function promptUser433() {
Swal.fire({
title: 'Serial Port:',
input: 'text',
inputAttributes: {
autocapitalize: 'off'
},
showCancelButton: true,
confirmButtonText: 'Submit',
cancelButtonText: 'Cancel',
showLoaderOnConfirm: true,
preConfirm: (value) => {
// Make an AJAX request to Flask
fetch(`/attach_serial_433?user_input=${encodeURIComponent(value)}`)
.then(response => response.json())
.then(data => {
Swal.fire({
title: 'Result',
text: data.result,
icon: 'info'
});
})
.catch(error => {
console.error('Error:', error);
Swal.fire('Oops!', 'Something went wrong.', 'error');
});
},
allowOutsideClick: () => !Swal.isLoading()
});
}
</script>
<script>
function deleteSerial433() {
Swal.fire({
title: 'Are you sure you want to disconnect the 433 MHz port?',
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Yes',
cancelButtonText: 'No',
}).then((result) => {
if (result.isConfirmed) {
// User clicked "Yes" - Send HTTP GET request to Flask
fetch('/delete_serial_433')
.then(response => response.json())
.then(data => {
Swal.fire({
title: 'Confirmation Result',
text: data.result,
icon: 'info'
});
})
.catch(error => {
console.error('Error:', error);
Swal.fire('Oops!', 'Something went wrong.', 'error');
});
}
});
}
</script>
<script>
function transmit433() {
Swal.fire({
title: 'Enter data to transmit:',
input: 'text',
inputAttributes: {
autocapitalize: 'off'
},
showCancelButton: true,
confirmButtonText: 'Submit',
cancelButtonText: 'Cancel',
showLoaderOnConfirm: true,
preConfirm: (value) => {
// Make an HTTP POST request to Flask
return fetch('/transmit433', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ user_input: value })
})
.then(response => response.json())
.catch(error => {
console.error('Error:', error);
Swal.fire('Oops!', 'Something went wrong. Maybe the serial port is disconnected?', 'error');
});
},
allowOutsideClick: () => !Swal.isLoading()
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: 'Data Transmitted',
icon: 'info'
});
}
});
}
</script>
</div>
</div>
</div>
<!-- Collapsible button -->
<a class="btn btn-primary" data-bs-toggle="collapse" href="#collapse433" role="button" aria-expanded="false" aria-controls="collapse433">
Configure 433 MHz Transceiver
</a>
</div>
<br><br>
<div class="resume-section-content">
<div class="collapse" id="collapse868">
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">868 MHz LoRa Transceiver</h3><br>
<div class="serial-data" id="serial-data-port2">
</div>
<div style="text-align:center;">
<button class="styled-button" onclick="promptUser868()">Connect Serial Port</button>
<button class="disconnect-button" onclick="deleteSerial868()">Disconnect Serial Port</button>
<button class="transmit-button" onclick="transmit868()">Transmit Data</button><br><br>
<button class="transmit-button" onclick="clearDataPort2()">Clear Data</button>
<input type="checkbox" id="autoscroll-port2" checked>
<label for="autoscroll-port2">Autoscroll</label>
<p id="status-label868" style="text-align: center">Status: Checking...</p>
<div class="indicator-container">
<div id="indicator868" class="indicator"></div>
</div>
</div>
</div>
<script>
function updateIndicator868() {
// Fetch the status from the Flask route
$.ajax({
url: '/checkSer',
type: 'GET',
data: {
port: 'port2',
},
success: function(response) {
if (response['result'] === 'True') {
$('#indicator868').removeClass('red').addClass('green');
$('#status-label868').text('Status: Connected');
} else {
$('#indicator868').removeClass('green').addClass('red');
$('#status-label868').text('Serial Port Status: Disconnected');
}
},
error: function() {
$('#indicator868').removeClass('green').addClass('red');
$('#status-label868a').text('Serial Port Status: Disconnected');
}
});
}
// Initial update of the indicator
updateIndicator868();
// Periodically check and update the indicator (e.g., every 5 seconds)
setInterval(updateIndicator868, 5000);
</script>
<script>
// For port2
var socket = io.connect('http://' + document.domain + ':' + location.port);
socket.on('initial_serial_data_port2', function(data) {
var serialDataDiv2 = document.getElementById('serial-data-port2');
data.data.forEach(function(line) {
serialDataDiv2.innerHTML += line + '<br>';
});
// Scroll to the bottom for new data
//serialDataDiv.scrollTop = serialDataDiv.scrollHeight;
});
socket.on('serial_data_port2', function(data) {
var serialDataDiv2 = document.getElementById('serial-data-port2');
serialDataDiv2.innerHTML += data.data + '<br>';
var autoscrollCheckbox2 = document.getElementById('autoscroll-port2');
if (autoscrollCheckbox2.checked) {
serialDataDiv2.scrollTop = serialDataDiv2.scrollHeight;
}
});
</script>
<script>
function promptUser868() {
Swal.fire({
title: 'Serial Port:',
input: 'text',
inputAttributes: {
autocapitalize: 'off'
},
showCancelButton: true,
confirmButtonText: 'Submit',
cancelButtonText: 'Cancel',
showLoaderOnConfirm: true,
preConfirm: (value) => {
// Make an AJAX request to Flask
fetch(`/attach_serial_868?user_input=${encodeURIComponent(value)}`)
.then(response => response.json())
.then(data => {
Swal.fire({
title: 'Result',
text: data.result,
icon: 'info'
});
})
.catch(error => {
console.error('Error:', error);
Swal.fire('Oops!', 'Something went wrong.', 'error');
});
},
allowOutsideClick: () => !Swal.isLoading()
});
}
</script>
<script>
function deleteSerial868() {
Swal.fire({
title: 'Are you sure you want to disconnect the 868 MHz port?',
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Yes',
cancelButtonText: 'No',
}).then((result) => {
if (result.isConfirmed) {
// User clicked "Yes" - Send HTTP GET request to Flask
fetch('/delete_serial_868')
.then(response => response.json())
.then(data => {
Swal.fire({
title: 'Confirmation Result',
text: data.result,
icon: 'info'
});
})
.catch(error => {
console.error('Error:', error);
Swal.fire('Oops!', 'Something went wrong.', 'error');
});
}
});
}
</script>
<script>
function transmit868() {
Swal.fire({
title: 'Enter data to transmit:',
input: 'text',
inputAttributes: {
autocapitalize: 'off'
},
showCancelButton: true,
confirmButtonText: 'Submit',
cancelButtonText: 'Cancel',
showLoaderOnConfirm: true,
preConfirm: (value) => {
// Make an HTTP POST request to Flask
return fetch('/transmit868', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ user_input: value })
})
.then(response => response.json())
.catch(error => {
console.error('Error:', error);
Swal.fire('Oops!', 'Something went wrong. Maybe the serial port is disconnected?', 'error');
});
},
allowOutsideClick: () => !Swal.isLoading()
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: 'Data Transmitted',
icon: 'info'
});
}
});
}
</script>
</div>
</div>
<!-- Collapsible button -->
<a class="btn btn-primary" data-bs-toggle="collapse" href="#collapse868" role="button" aria-expanded="false" aria-controls="collapse868">
Configure 868 MHz Transceiver
</a>
</div>
<br><br>
<div class="resume-section-content">
<div class="collapse" id="collapse915">
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">915 MHZ LoRa Transceiver</h3><br>
<div class="serial-data" id="serial-data-port3">
</div>
<div style="text-align:center;">
<button class="styled-button" onclick="promptUser915()">Connect Serial Port</button>
<button class="disconnect-button" onclick="deleteSerial915()">Disconnect Serial Port</button>
<button class="transmit-button" onclick="transmit915()">Transmit Data</button><br><br>
<button class="transmit-button" onclick="clearDataPort3()">Clear Data</button>
<input type="checkbox" id="autoscroll-port3" checked>
<label for="autoscroll-port3">Autoscroll</label>
<p id="status-label915" style="text-align: center">Status: Checking...</p>
<div class="indicator-container">
<div id="indicator915" class="indicator"></div>
</div>
</div>
</div>
<script>
function updateIndicator915() {
// Fetch the status from the Flask route
$.ajax({
url: '/checkSer',
type: 'GET',
data: {
port: 'port3',
},
success: function(response) {
if (response['result'] === 'True') {
$('#indicator915').removeClass('red').addClass('green');
$('#status-label915').text('Status: Connected');
} else {
$('#indicator915').removeClass('green').addClass('red');
$('#status-label915').text('Serial Port Status: Disconnected');
}
},
error: function() {
$('#indicator915').removeClass('green').addClass('red');
$('#status-label915').text('Serial Port Status: Disconnected');
}
});
}
// Initial update of the indicator
updateIndicator915();
// Periodically check and update the indicator (e.g., every 5 seconds)
setInterval(updateIndicator915, 5000);
</script>
<script>
// For port3
var socket = io.connect('http://' + document.domain + ':' + location.port);
socket.on('initial_serial_data_port3', function(data) {
var serialDataDiv3 = document.getElementById('serial-data-port3');
data.data.forEach(function(line) {
serialDataDiv3.innerHTML += line + '<br>';
});
// Scroll to the bottom for new data
//serialDataDiv.scrollTop = serialDataDiv.scrollHeight;
});
socket.on('serial_data_port3', function(data) {
var serialDataDiv3 = document.getElementById('serial-data-port3');
serialDataDiv3.innerHTML += data.data + '<br>';
var autoscrollCheckbox3 = document.getElementById('autoscroll-port3');
if (autoscrollCheckbox3.checked) {
serialDataDiv3.scrollTop = serialDataDiv3.scrollHeight;
}
});
</script>
<script>
function promptUser915() {
Swal.fire({
title: 'Serial Port:',
input: 'text',
inputAttributes: {
autocapitalize: 'off'
},
showCancelButton: true,
confirmButtonText: 'Submit',
cancelButtonText: 'Cancel',
showLoaderOnConfirm: true,
preConfirm: (value) => {
// Make an AJAX request to Flask
fetch(`/attach_serial_915?user_input=${encodeURIComponent(value)}`)
.then(response => response.json())
.then(data => {
Swal.fire({
title: 'Result',
text: data.result,
icon: 'info'
});
})
.catch(error => {
console.error('Error:', error);
Swal.fire('Oops!', 'Something went wrong.', 'error');
});
},
allowOutsideClick: () => !Swal.isLoading()
});
}
</script>
<script>
function deleteSerial915() {
Swal.fire({
title: 'Are you sure you want to disconnect the 915 MHz port?',
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Yes',
cancelButtonText: 'No',
}).then((result) => {
if (result.isConfirmed) {
// User clicked "Yes" - Send HTTP GET request to Flask
fetch('/delete_serial_915')
.then(response => response.json())
.then(data => {
Swal.fire({
title: 'Confirmation Result',
text: data.result,
icon: 'info'
});
})
.catch(error => {
console.error('Error:', error);
Swal.fire('Oops!', 'Something went wrong.', 'error');
});
}
});
}
</script>
<script>
function transmit915() {
Swal.fire({
title: 'Enter data to transmit:',
input: 'text',
inputAttributes: {
autocapitalize: 'off'
},
showCancelButton: true,
confirmButtonText: 'Submit',
cancelButtonText: 'Cancel',
showLoaderOnConfirm: true,
preConfirm: (value) => {
// Make an HTTP POST request to Flask
return fetch('/transmit915', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ user_input: value })
})
.then(response => response.json())
.catch(error => {
console.error('Error:', error);
Swal.fire('Oops!', 'Something went wrong. Maybe the serial port is disconnected?', 'error');
});
},
allowOutsideClick: () => !Swal.isLoading()
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: 'Data Transmitted',
icon: 'info'
});
}
});
}
</script>
</div>
</div>
<!-- Collapsible button -->
<a class="btn btn-primary" data-bs-toggle="collapse" href="#collapse915" role="button" aria-expanded="false" aria-controls="collapse915">
Configure 915 MHz Transceiver
</a>
</div>
<br>
<br>
<div class="config-section">
<h4>Configure LoRaWAN Gateways</h4>
<p>
The <strong>'Configure LoRaWAN Gateway'</strong> section allows you to set up to ten Dragino LPS8N LoRaWAN gateways.
</p>
<ul>
<li>Click the "Configure Gateway" button to start configuring each gateway's IP address.</li>
<li>Skip configuring a gateway or disconnect an existing one by leaving the input field empty.</li>
<li>All entered IP addresses are validated for correct formatting.</li>
<li>Once configured, the application automatically retrieves and stores LoRaWAN traffic from each active gateway.</li>
<li>Access and analyze stored traffic in 'survey mode'.</li>
</ul>
</div>
<br>
<button class="btn btn-primary" id="configureGatewayBtn">Configure LoRaWAN Gateway</button>
<script>
document.getElementById('configureGatewayBtn').addEventListener('click', function() {
let gatewayIPs = {};
const ipPrompt = (title) => {
return Swal.fire({
title: title,
input: 'text',
inputPlaceholder: 'Leave empty to keep current IP',
inputValidator: (value) => {
if (value && !value.match(/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/)) {
return 'Please enter a valid IP address or leave it empty';
}
}
});
};
const promptGateways = async () => {
for (let i = 1; i <= 10; i++) {
const result = await ipPrompt(`Enter Gateway ${i} IP Address`);
if (result.value) gatewayIPs[`gateway${i}`] = result.value;
}
// Now send the IPs to the server only if they are not undefined
let queryString = Object.keys(gatewayIPs).reduce((acc, key) => {
if (gatewayIPs[key] !== undefined) {
acc.push(`${key}=${encodeURIComponent(gatewayIPs[key])}`);
}
return acc;
}, []).join('&');
if (queryString) {
fetch('/set_gateways', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: queryString
})
.then(response => response.json())
.then(data => Swal.fire('Success', 'Gateway IPs updated successfully', 'success'))
.catch(error => Swal.fire('Error', 'There was an issue updating the Gateway IPs', 'error'));
} else {
Swal.fire('No Changes', 'No IP addresses were changed.', 'info');
}
};
promptGateways();
});
</script>
</div>
</section>
</div>
<script>
document.getElementById('configureGatewayBtn').addEventListener('click', function() {
let gatewayIPs = {};
const ipPrompt = (title) => {
return Swal.fire({
title: title,
input: 'text',
inputPlaceholder: 'Leave empty to keep current IP',
inputValidator: (value) => {
if (value && !value.match(/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/)) {
return 'Please enter a valid IP address or leave it empty';
}
}
});
};
const promptGateways = async () => {
for (let i = 1; i <= 10; i++) {
const result = await ipPrompt(`Enter Gateway ${i} IP Address`);
if (result.value) gatewayIPs[`gateway${i}`] = result.value;
}
// Now send the IPs to the server only if they are not undefined
let queryString = Object.keys(gatewayIPs).reduce((acc, key) => {
if (gatewayIPs[key] !== undefined) {
acc.push(`${key}=${encodeURIComponent(gatewayIPs[key])}`);
}
return acc;
}, []).join('&');
if (queryString) {
fetch('/set_gateways', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: queryString
})
.then(response => response.json())
.then(data => Swal.fire('Success', 'Gateway IPs updated successfully', 'success'))
.catch(error => Swal.fire('Error', 'There was an issue updating the Gateway IPs', 'error'));
} else {
Swal.fire('No Changes', 'No IP addresses were changed.', 'info');
}
};
promptGateways();
});
</script>
<!-- Bootstrap core JS-->
<script src="static/js/bootstrap.bundle.min.js"></script>
<!-- Core theme JS-->
+1
View File
@@ -33,6 +33,7 @@
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/#config">Configuration</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/analysis">Analysis Mode</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#">Survey Mode</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/tracking">Tracking Mode</a></li>
+1
View File
@@ -33,6 +33,7 @@
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/#config">Configuration</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/analysis">Analysis Mode</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/survey">Survey Mode</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#">Tracking Mode</a></li>