database
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
logs/app.log
|
logs/*.log
|
||||||
logs/loop.log
|
logs/loop.log
|
||||||
deviceID.txt
|
deviceID.txt
|
||||||
loop/loop.log
|
loop/loop.log
|
||||||
|
|||||||
@@ -2,9 +2,13 @@
|
|||||||
"loop_activation": true,
|
"loop_activation": true,
|
||||||
"loop_log": true,
|
"loop_log": true,
|
||||||
"boot_log": true,
|
"boot_log": true,
|
||||||
|
"NPM/get_data.py": true,
|
||||||
|
"tests/script2.py": false,
|
||||||
|
"tests/script3.py": true,
|
||||||
"deviceID": "XXXX",
|
"deviceID": "XXXX",
|
||||||
"deviceName": "NebuleAir-proXXX",
|
"deviceName": "NebuleAir-proXXX",
|
||||||
"SaraR4_baudrate": 115200,
|
"SaraR4_baudrate": 115200,
|
||||||
|
"NPM_solo_port": "/dev/ttyAMA5",
|
||||||
"NextPM_ports": [
|
"NextPM_ports": [
|
||||||
"ttyAMA5"
|
"ttyAMA5"
|
||||||
],
|
],
|
||||||
|
|||||||
175
html/database.html
Normal file
175
html/database.html
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>NebuleAir</title>
|
||||||
|
<link rel="stylesheet" href="assets/css/bootstrap.min.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
#sidebar a.nav-link {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
#sidebar a.nav-link:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
#sidebar a.nav-link svg {
|
||||||
|
margin-right: 8px; /* Add spacing between icons and text */
|
||||||
|
}
|
||||||
|
#sidebar {
|
||||||
|
transition: transform 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
.offcanvas-backdrop {
|
||||||
|
z-index: 1040;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Topbar -->
|
||||||
|
<span id="topbar"></span>
|
||||||
|
|
||||||
|
<!-- Sidebar Offcanvas for Mobile -->
|
||||||
|
<div class="offcanvas offcanvas-start text-white bg-dark" tabindex="-1" id="sidebarOffcanvas" aria-labelledby="sidebarOffcanvasLabel">
|
||||||
|
<div class="offcanvas-header">
|
||||||
|
<h5 class="offcanvas-title" id="sidebarOffcanvasLabel">NebuleAir</h5>
|
||||||
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="offcanvas-body" id="sidebar_mobile">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container-fluid mt-5">
|
||||||
|
<div class="row">
|
||||||
|
<aside class="col-md-2 col-lg-1 d-none d-md-block vh-100 position-fixed bg-dark text-white" id="sidebar">
|
||||||
|
</aside>
|
||||||
|
<!-- Main content -->
|
||||||
|
<main class="col-md-10 ms-sm-auto col-lg-11 offset-md-2 offset-lg-1 px-md-4">
|
||||||
|
<h1 class="mt-4">Base de données</h1>
|
||||||
|
<p>Le capteur enregistre en local les données de mesures. Vous pouvez ici les consulter et les télécharger.</p>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="card text-dark bg-light">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Consulter Base de donnée</h5>
|
||||||
|
<p class="card-text">General information.</p>
|
||||||
|
<button class="btn btn-primary" onclick="get_internet()">Get Data</button>
|
||||||
|
<table class="table table-striped-columns">
|
||||||
|
<tbody id="data-table-body_internet_general"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="card text-dark bg-light">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Télécharger les données</h5>
|
||||||
|
<p class="card-text">Scan des réseaux WIFI disponibles.</p>
|
||||||
|
<button class="btn btn-primary" onclick="wifi_scan()">Download</button>
|
||||||
|
<table class="table">
|
||||||
|
<tbody id="data-table-body_wifi_scan"></tbody>
|
||||||
|
</table>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- JAVASCRIPT -->
|
||||||
|
|
||||||
|
<!-- Link Ajax locally -->
|
||||||
|
<script src="assets/jquery/jquery-3.7.1.min.js"></script>
|
||||||
|
<!-- Link Bootstrap JS and Popper.js locally -->
|
||||||
|
<script src="assets/js/bootstrap.bundle.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
console.log("DOMContentLoaded");
|
||||||
|
|
||||||
|
const elementsToLoad = [
|
||||||
|
{ id: 'topbar', file: 'topbar.html' },
|
||||||
|
{ id: 'sidebar', file: 'sidebar.html' },
|
||||||
|
{ id: 'sidebar_mobile', file: 'sidebar.html' }
|
||||||
|
];
|
||||||
|
|
||||||
|
elementsToLoad.forEach(({ id, file }) => {
|
||||||
|
fetch(file)
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(data => {
|
||||||
|
const element = document.getElementById(id);
|
||||||
|
if (element) {
|
||||||
|
element.innerHTML = data;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error(`Error loading ${file}:`, error));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
fetch('../config.json') // Replace 'deviceID.txt' with 'config.json'
|
||||||
|
.then(response => response.json()) // Parse response as JSON
|
||||||
|
.then(data => {
|
||||||
|
console.log("Getting config file (onload)");
|
||||||
|
//get device ID
|
||||||
|
const deviceID = data.deviceID.trim().toUpperCase();
|
||||||
|
//document.getElementById('pageTitle_plus_ID').innerText = 'token: ' + deviceID;
|
||||||
|
|
||||||
|
//get device Name
|
||||||
|
const deviceName = data.deviceName;
|
||||||
|
|
||||||
|
const elements = document.querySelectorAll('.sideBar_sensorName');
|
||||||
|
elements.forEach((element) => {
|
||||||
|
element.innerText = deviceName;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//get local RTC
|
||||||
|
$.ajax({
|
||||||
|
url: 'launcher.php?type=RTC_time',
|
||||||
|
dataType: 'text', // Specify that you expect a JSON response
|
||||||
|
method: 'GET', // Use GET or POST depending on your needs
|
||||||
|
success: function(response) {
|
||||||
|
console.log("Local RTC: " + response);
|
||||||
|
const RTC_Element = document.getElementById("RTC_time");
|
||||||
|
RTC_Element.textContent = response;
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
console.error('AJAX request failed:', status, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error loading config.json:', error));
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -64,6 +64,7 @@
|
|||||||
<div id="disk_space"></div>
|
<div id="disk_space"></div>
|
||||||
<p class="card-text">Memory usage (total size <span id="memory_size"></span> Mb) </p>
|
<p class="card-text">Memory usage (total size <span id="memory_size"></span> Mb) </p>
|
||||||
<div id="memory_space"></div>
|
<div id="memory_space"></div>
|
||||||
|
<p class="card-text"> Database size: <span id="database_size"></span> </p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -73,10 +74,7 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Mesures PM</h5>
|
<h5 class="card-title">Mesures PM</h5>
|
||||||
<p class="card-text">Particules Fines </p>
|
<canvas id="sensorPMChart" style="width: 100%; max-width: 600px; height: 200px;"></canvas>
|
||||||
<canvas id="sensorPMChart" width="400" height="200"></canvas>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -152,6 +150,34 @@ window.onload = function() {
|
|||||||
console.error('AJAX request failed:', status, error);
|
console.error('AJAX request failed:', status, error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//get database size
|
||||||
|
$.ajax({
|
||||||
|
url: 'launcher.php?type=database_size',
|
||||||
|
dataType: 'json', // Specify that you expect a JSON response
|
||||||
|
method: 'GET', // Use GET or POST depending on your needs
|
||||||
|
success: function(response) {
|
||||||
|
console.log(response);
|
||||||
|
|
||||||
|
if (response.size_megabytes !== undefined) {
|
||||||
|
// Extract and format the size in MB
|
||||||
|
const databaseSizeMB = response.size_megabytes + " MB";
|
||||||
|
|
||||||
|
// Update the HTML element with the database size
|
||||||
|
const databaseSizeElement = document.getElementById("database_size");
|
||||||
|
databaseSizeElement.textContent = databaseSizeMB;
|
||||||
|
|
||||||
|
console.log("Database size:", databaseSizeMB);
|
||||||
|
} else if (response.error) {
|
||||||
|
// Handle errors from the PHP response
|
||||||
|
console.error("Error from server:", response.error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
console.error('AJAX request failed:', status, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
//get disk free space
|
//get disk free space
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -289,7 +315,41 @@ window.onload = function() {
|
|||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false
|
maintainAspectRatio: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'top'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Time'
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
autoSkip: true,
|
||||||
|
maxTicksLimit: 5,
|
||||||
|
callback: function(value, index) {
|
||||||
|
// Access the correct label from the `labels` array
|
||||||
|
const label = labels[index]; // Use the original `labels` array
|
||||||
|
if (label && typeof label === 'string' && label.includes(' ')) {
|
||||||
|
return label.split(' ')[1].slice(0, 5); // Extract "HH:MM"
|
||||||
|
}
|
||||||
|
return value; // Fallback for invalid labels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Values (µg/m³)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ if ($type == "get_npm_sqlite_data") {
|
|||||||
$db = new PDO("sqlite:$database_path");
|
$db = new PDO("sqlite:$database_path");
|
||||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
// Fetch the last 10 records
|
// Fetch the last 30 records
|
||||||
$stmt = $db->query("SELECT timestamp, PM1, PM25, PM10 FROM data ORDER BY timestamp DESC LIMIT 10");
|
$stmt = $db->query("SELECT timestamp, PM1, PM25, PM10 FROM data ORDER BY timestamp DESC LIMIT 30");
|
||||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
echo json_encode($data);
|
echo json_encode($data);
|
||||||
@@ -92,6 +92,57 @@ if ($type == "clear_loopLogs") {
|
|||||||
echo $output;
|
echo $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($type == "database_size") {
|
||||||
|
|
||||||
|
// Path to the SQLite database file
|
||||||
|
$databasePath = '/var/www/nebuleair_pro_4g/sqlite/sensors.db';
|
||||||
|
|
||||||
|
// Check if the file exists
|
||||||
|
if (file_exists($databasePath)) {
|
||||||
|
try {
|
||||||
|
// Connect to the SQLite database
|
||||||
|
$db = new PDO("sqlite:$databasePath");
|
||||||
|
|
||||||
|
// Get the file size in bytes
|
||||||
|
$fileSizeBytes = filesize($databasePath);
|
||||||
|
|
||||||
|
// Convert the file size to human-readable formats
|
||||||
|
$fileSizeKilobytes = $fileSizeBytes / 1024; // KB
|
||||||
|
$fileSizeMegabytes = $fileSizeKilobytes / 1024; // MB
|
||||||
|
|
||||||
|
// Query the number of records in the `data` table
|
||||||
|
$query = "SELECT COUNT(*) AS total_records FROM data";
|
||||||
|
$result = $db->query($query);
|
||||||
|
$recordCount = $result ? $result->fetch(PDO::FETCH_ASSOC)['total_records'] : 0;
|
||||||
|
|
||||||
|
// Prepare the JSON response
|
||||||
|
$data = [
|
||||||
|
'path' => $databasePath,
|
||||||
|
'size_bytes' => $fileSizeBytes,
|
||||||
|
'size_kilobytes' => round($fileSizeKilobytes, 2),
|
||||||
|
'size_megabytes' => round($fileSizeMegabytes, 2),
|
||||||
|
'data_table_records' => $recordCount
|
||||||
|
];
|
||||||
|
|
||||||
|
// Output the JSON response
|
||||||
|
echo json_encode($data, JSON_PRETTY_PRINT);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Handle database connection errors
|
||||||
|
echo json_encode([
|
||||||
|
'error' => 'Database query failed: ' . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle error if the file doesn't exist
|
||||||
|
echo json_encode([
|
||||||
|
'error' => 'Database file not found',
|
||||||
|
'path' => $databasePath
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if ($type == "linux_disk") {
|
if ($type == "linux_disk") {
|
||||||
$command = 'df -h /';
|
$command = 'df -h /';
|
||||||
$output = shell_exec($command);
|
$output = shell_exec($command);
|
||||||
|
|||||||
@@ -13,6 +13,13 @@
|
|||||||
</svg>
|
</svg>
|
||||||
Capteurs
|
Capteurs
|
||||||
</a>
|
</a>
|
||||||
|
<a class="nav-link text-white" href="database.html">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-database" viewBox="0 0 16 16">
|
||||||
|
<path d="M4.318 2.687C5.234 2.271 6.536 2 8 2s2.766.27 3.682.687C12.644 3.125 13 3.627 13 4c0 .374-.356.875-1.318 1.313C10.766 5.729 9.464 6 8 6s-2.766-.27-3.682-.687C3.356 4.875 3 4.373 3 4c0-.374.356-.875 1.318-1.313M13 5.698V7c0 .374-.356.875-1.318 1.313C10.766 8.729 9.464 9 8 9s-2.766-.27-3.682-.687C3.356 7.875 3 7.373 3 7V5.698c.271.202.58.378.904.525C4.978 6.711 6.427 7 8 7s3.022-.289 4.096-.777A5 5 0 0 0 13 5.698M14 4c0-1.007-.875-1.755-1.904-2.223C11.022 1.289 9.573 1 8 1s-3.022.289-4.096.777C2.875 2.245 2 2.993 2 4v9c0 1.007.875 1.755 1.904 2.223C4.978 15.71 6.427 16 8 16s3.022-.289 4.096-.777C13.125 14.755 14 14.007 14 13zm-1 4.698V10c0 .374-.356.875-1.318 1.313C10.766 11.729 9.464 12 8 12s-2.766-.27-3.682-.687C3.356 10.875 3 10.373 3 10V8.698c.271.202.58.378.904.525C4.978 9.71 6.427 10 8 10s3.022-.289 4.096-.777A5 5 0 0 0 13 8.698m0 3V13c0 .374-.356.875-1.318 1.313C10.766 14.729 9.464 15 8 15s-2.766-.27-3.682-.687C3.356 13.875 3 13.373 3 13v-1.302c.271.202.58.378.904.525C4.978 12.71 6.427 13 8 13s3.022-.289 4.096-.777c.324-.147.633-.323.904-.525"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
DataBase
|
||||||
|
</a>
|
||||||
<a class="nav-link text-white" href="saraR4.html">
|
<a class="nav-link text-white" href="saraR4.html">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-reception-4" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-reception-4" viewBox="0 0 16 16">
|
||||||
<path d="M0 11.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v11a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5z"/>
|
<path d="M0 11.5a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5zm4-3a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 .5.5v11a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5z"/>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Script to create a sqlite database
|
|||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
# Connect to (or create) the database
|
# Connect to (or create if not existent) the database
|
||||||
conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
|
conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user