mirror of
https://github.com/skinnyrad/Lora-Scanner.git
synced 2026-07-07 02:01:12 +02:00
Merge pull request #16 from skinnyrad/refactor/gatewayip
Refactor/gatewayip
This commit is contained in:
@@ -71,12 +71,13 @@ From the configuration page, you can connect to a LoRa transmitter to start send
|
|||||||
|
|
||||||
The 'Configure LoRaWAN Gateway' section allows you to set up to ten Dragino LPS8N LoRaWAN gateways.
|
The 'Configure LoRaWAN Gateway' section allows you to set up to ten Dragino LPS8N LoRaWAN gateways.
|
||||||
|
|
||||||
- Click the "Configure Gateway" button to start configuring each gateway's IP address.
|
- Click the "Update Gateways" button to update each gateway's IP address.
|
||||||
- Skip configuring a gateway or disconnect an existing one by leaving the input field empty.
|
- Skip configuring a gateway by leaving the input field empty.
|
||||||
- All entered IP addresses are validated for correct formatting.
|
- All entered IP addresses are validated for correct formatting.
|
||||||
- Once configured, the application automatically retrieves and stores LoRaWAN traffic from each active gateway.
|
- Once configured, the application automatically retrieves and stores LoRaWAN traffic from each active gateway.
|
||||||
- Access and analyze stored traffic in 'survey mode'.
|
- Access and analyze stored traffic in 'survey mode'.
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Analysis Mode
|
## Analysis Mode
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ def index():
|
|||||||
Returns:
|
Returns:
|
||||||
str: The rendered HTML content for the index page.
|
str: The rendered HTML content for the index page.
|
||||||
"""
|
"""
|
||||||
return render_template('index.html')
|
return render_template('index.html', gateway_ips=gateway_ips)
|
||||||
|
|
||||||
@app.route('/analysis')
|
@app.route('/analysis')
|
||||||
def analysis():
|
def analysis():
|
||||||
@@ -727,20 +727,25 @@ def set_gateways():
|
|||||||
"""
|
"""
|
||||||
global gateway_ips
|
global gateway_ips
|
||||||
data = request.form
|
data = request.form
|
||||||
for key in [f'gateway{i}' for i in range(1, 11)]:
|
|
||||||
|
# Create an ordered dictionary of gateways
|
||||||
|
ordered_gateways = {}
|
||||||
|
for i in range(1, 11):
|
||||||
|
key = f'gateway{i}'
|
||||||
input_ip = data.get(key, '').strip()
|
input_ip = data.get(key, '').strip()
|
||||||
if input_ip:
|
ordered_gateways[key] = input_ip
|
||||||
try:
|
|
||||||
ipaddress.ip_address(input_ip)
|
# Update the global gateway_ips with ordered data
|
||||||
gateway_ips[key] = input_ip
|
gateway_ips.update(ordered_gateways)
|
||||||
except ValueError:
|
|
||||||
return jsonify({"error": f"Invalid IP address provided for {key}"}), 400
|
|
||||||
|
|
||||||
for gateway, ip_address in gateway_ips.items():
|
for gateway, ip_address in gateway_ips.items():
|
||||||
print(f"Gateway {gateway} has IP address: {ip_address}")
|
print(f"Gateway {gateway} has IP address: {ip_address}")
|
||||||
|
|
||||||
return jsonify({"message": "Gateway IPs updated successfully"}), 200
|
return jsonify({"message": "Gateway IPs updated successfully"}), 200
|
||||||
|
|
||||||
|
@app.route('/get_gateways', methods=['GET'])
|
||||||
|
def get_gateways():
|
||||||
|
return jsonify(gateway_ips)
|
||||||
|
|
||||||
@app.route('/downloadPackets', methods=['GET'])
|
@app.route('/downloadPackets', methods=['GET'])
|
||||||
def downloadPackets():
|
def downloadPackets():
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 138 KiB |
+218
-59
@@ -202,7 +202,7 @@
|
|||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
Swal.fire('Oops!', 'Something went wrong. The port might already be in use.', 'error');
|
Swal.fire('Oops!', 'Something went wrong.', 'error');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -398,7 +398,7 @@
|
|||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
Swal.fire('Oops!', 'Something went wrong. The port might already be in use.', 'error');
|
Swal.fire('Oops!', 'Something went wrong.', 'error');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -594,7 +594,7 @@
|
|||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
Swal.fire('Oops!', 'Something went wrong. The port might already be in use.', 'error');
|
Swal.fire('Oops!', 'Something went wrong.', 'error');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -682,75 +682,234 @@
|
|||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div class="config-section">
|
<section class="config-section">
|
||||||
<h4>Configure LoRaWAN Gateways</h4>
|
<h4>Configure LoRaWAN Gateways</h4>
|
||||||
|
<div class="description">
|
||||||
<p>
|
<p>
|
||||||
The <strong>'Configure LoRaWAN Gateway'</strong> section allows you to set up to ten Dragino LPS8N LoRaWAN gateways.
|
The <strong>Configure LoRaWAN Gateway</strong> section allows you to set up to ten Dragino LPS8N LoRaWAN gateways.
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Click the "Configure Gateway" button to start configuring each gateway's IP address.</li>
|
<li>Enter the IP address for each gateway you want to configure.</li>
|
||||||
<li>Skip configuring a gateway or disconnect an existing one by leaving the input field empty.</li>
|
<li>Leave the input field empty to keep the current IP or disconnect an existing one.</li>
|
||||||
<li>All entered IP addresses are validated for correct formatting.</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>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>
|
<li>Access and analyze stored traffic in 'survey mode'.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<p class="note"><em>Empty values will be ignored, and the Gateway IP address will remain unchanged</em></p>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<form id="gatewayForm" class="mt-3">
|
||||||
<button class="btn btn-primary" id="configureGatewayBtn">Configure LoRaWAN Gateway</button>
|
<div id="gatewayInputs"></div>
|
||||||
|
<button type="button" class="btn btn-primary mt-3" onclick="submitGatewayForm()">Update Gateways</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
<script>
|
<style>
|
||||||
document.getElementById('configureGatewayBtn').addEventListener('click', function() {
|
.gateway-container {
|
||||||
let gatewayIPs = {};
|
position: relative;
|
||||||
|
}
|
||||||
const ipPrompt = (title) => {
|
|
||||||
return Swal.fire({
|
.status-indicator {
|
||||||
title: title,
|
transition: background-color 0.3s ease;
|
||||||
input: 'text',
|
box-shadow: 0 0 5px rgba(0,0,0,0.2);
|
||||||
inputPlaceholder: 'Leave empty to keep current IP',
|
}
|
||||||
inputValidator: (value) => {
|
|
||||||
if (value && !value.match(/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/)) {
|
.status-indicator[title] {
|
||||||
return 'Please enter a valid IP address or leave it empty';
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
font-style: italic;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-section {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
class GatewayManager {
|
||||||
|
constructor() {
|
||||||
|
this.gatewayIPs = Object.fromEntries(
|
||||||
|
Array.from({length: 10}, (_, i) => [`gateway${i + 1}`, ''])
|
||||||
|
);
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
createGatewayInputs() {
|
||||||
|
const container = document.getElementById('gatewayInputs');
|
||||||
|
container.innerHTML = Array.from({length: 10}, (_, i) => {
|
||||||
|
const key = `gateway${i + 1}`;
|
||||||
|
return `
|
||||||
|
<div class="gateway-container d-flex align-items-center mb-2">
|
||||||
|
<input type="text"
|
||||||
|
id="${key}"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Gateway ${i + 1} IP Address"
|
||||||
|
value="${this.gatewayIPs[key]}">
|
||||||
|
<div id="${key}-status" class="status-indicator ml-2"
|
||||||
|
style="width: 12px; height: 12px; border-radius: 50%; margin-left: 10px;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async checkGatewayStatus(gatewayIP) {
|
||||||
|
if (!gatewayIP) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`http://${gatewayIP}:8000/cgi-bin/log-traffic.has`, {
|
||||||
|
method: 'HEAD',
|
||||||
|
mode: 'no-cors',
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatusIndicator(gatewayKey, isOnline) {
|
||||||
|
const statusElement = document.getElementById(`${gatewayKey}-status`);
|
||||||
|
if (statusElement) {
|
||||||
|
statusElement.style.backgroundColor = isOnline ? '#4BD28F' : '#FF4D4D';
|
||||||
|
statusElement.title = isOnline ? 'Gateway Online' : 'Gateway Offline';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkAllGateways() {
|
||||||
|
for (const [key, ip] of Object.entries(this.gatewayIPs)) {
|
||||||
|
if (ip) {
|
||||||
|
const isOnline = await this.checkGatewayStatus(ip);
|
||||||
|
this.updateStatusIndicator(key, isOnline);
|
||||||
|
} else {
|
||||||
|
this.updateStatusIndicator(key, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateIPAddress(ip) {
|
||||||
|
if (!ip) return true;
|
||||||
|
const regex = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/;
|
||||||
|
if (!regex.test(ip)) return false;
|
||||||
|
|
||||||
|
return ip.split('.').every(octet => {
|
||||||
|
const num = parseInt(octet);
|
||||||
|
return num >= 0 && num <= 255;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async submitGatewayForm() {
|
||||||
|
const formData = new FormData();
|
||||||
|
let hasValidationError = false;
|
||||||
|
|
||||||
|
// Explicitly set each gateway value in the correct order
|
||||||
|
for (let i = 1; i <= 10; i++) {
|
||||||
|
const key = `gateway${i}`;
|
||||||
|
const input = document.getElementById(key);
|
||||||
|
const value = input.value.trim();
|
||||||
|
|
||||||
|
if (value && !this.validateIPAddress(value)) {
|
||||||
|
hasValidationError = true;
|
||||||
|
input.classList.add('is-invalid');
|
||||||
|
Swal.fire('Error', `Invalid IP address for ${key}`, 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always append the key to FormData, even if empty
|
||||||
|
formData.append(key, value);
|
||||||
|
if (value) {
|
||||||
|
input.classList.remove('is-invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasValidationError) return;
|
||||||
|
console.log(formData)
|
||||||
|
try {
|
||||||
|
const response = await fetch('/set_gateways', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error('Network response was not ok');
|
||||||
|
|
||||||
|
await response.json();
|
||||||
|
Swal.fire('Success', 'Gateway IPs updated successfully', 'success');
|
||||||
|
|
||||||
|
// Update local state with new values
|
||||||
|
for (let i = 1; i <= 10; i++) {
|
||||||
|
const key = `gateway${i}`;
|
||||||
|
this.gatewayIPs[key] = document.getElementById(key).value.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check status of all gateways after successful update
|
||||||
|
await this.checkAllGateways();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
Swal.fire('Error', 'There was an issue updating the Gateway IPs', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setupEventListeners() {
|
||||||
|
document.querySelector('#gatewayForm button')
|
||||||
|
.addEventListener('click', () => this.submitGatewayForm());
|
||||||
|
|
||||||
|
Object.keys(this.gatewayIPs).forEach(key => {
|
||||||
|
const input = document.getElementById(key);
|
||||||
|
if (input) {
|
||||||
|
input.addEventListener('change', async () => {
|
||||||
|
const ip = input.value.trim();
|
||||||
|
if (ip) {
|
||||||
|
const isOnline = await this.checkGatewayStatus(ip);
|
||||||
|
this.updateStatusIndicator(key, isOnline);
|
||||||
|
} else {
|
||||||
|
this.updateStatusIndicator(key, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
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>
|
}
|
||||||
|
async loadGatewayIPs() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/get_gateways');
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch gateway IPs');
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
this.gatewayIPs = data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading gateway IPs:', error);
|
||||||
|
Swal.fire('Error', 'Failed to load gateway configurations', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
await this.loadGatewayIPs(); // Load IPs first
|
||||||
|
this.createGatewayInputs(); // Then create inputs with loaded values
|
||||||
|
this.setupEventListeners();
|
||||||
|
await this.checkAllGateways(); // Check gateway statuses
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the gateway manager
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
new GatewayManager();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user