Add initial KioskCore and API endpoint for data analysis
- Created a new CSS file for kiosk styles, defining variables, typography, and layout for cards and toolbars. - Implemented new routes for data anlaysis page
This commit is contained in:
@@ -60,6 +60,64 @@ async function query(bucket, relativeTime, measurement, sensor, field) {
|
||||
|
||||
}
|
||||
|
||||
const sessionBucket = process.env.INFLX_BUCKET || 'logs';
|
||||
|
||||
/**
|
||||
* Query storica per una sessione di registrazione.
|
||||
* @param {string} sensor - nome sensore
|
||||
* @param {string} session - session_id (tag InfluxDB)
|
||||
* @param {string} since - ISO timestamp o duration (es. "-30d")
|
||||
* @param {string|null} until - ISO timestamp fine (opzionale)
|
||||
* @returns {Promise<Array<Object>>}
|
||||
*/
|
||||
async function querySessionHistory(sensor, session, since, until = null) {
|
||||
const rangeStr = until ? `start: ${since}, stop: ${until}` : `start: ${since}`;
|
||||
const fluxQuery = `
|
||||
from(bucket: "${sessionBucket}")
|
||||
|> range(${rangeStr})
|
||||
|> filter(fn: (r) => r._measurement == "logs")
|
||||
|> filter(fn: (r) => r.sensor == "${sensor}")
|
||||
|> filter(fn: (r) => r.session == "${session}")
|
||||
|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
|
||||
|> sort(columns: ["_time"])
|
||||
`;
|
||||
const rows = [];
|
||||
return new Promise((resolve, reject) => {
|
||||
querying.queryRows(fluxQuery, {
|
||||
next(row, tableMeta) { rows.push(tableMeta.toObject(row)); },
|
||||
error: reject,
|
||||
complete() { resolve(rows); },
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Esporta i dati di una sessione come stringa CSV.
|
||||
* @param {string} sensor
|
||||
* @param {string} session
|
||||
* @param {string} since
|
||||
* @param {string|null} until
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function exportSessionCSV(sensor, session, since, until = null) {
|
||||
const rows = await querySessionHistory(sensor, session, since, until);
|
||||
if (rows.length === 0) return '';
|
||||
const metaKeys = new Set(['result', 'table', '_start', '_stop', '_measurement', 'sensor', 'session', '']);
|
||||
const fieldNames = new Set();
|
||||
for (const row of rows) {
|
||||
for (const key of Object.keys(row)) {
|
||||
if (!metaKeys.has(key) && key !== '_time') fieldNames.add(key);
|
||||
}
|
||||
}
|
||||
const fields = Array.from(fieldNames).sort();
|
||||
const header = ['timestamp', ...fields].join(',');
|
||||
const csvRows = rows.map(row => {
|
||||
const values = fields.map(f => { const v = row[f]; return (v == null) ? '' : v; });
|
||||
return [row._time || '', ...values].join(',');
|
||||
});
|
||||
return header + '\n' + csvRows.join('\n') + '\n';
|
||||
}
|
||||
|
||||
async function checkInflux() {
|
||||
try {
|
||||
const result = await querying.collectRows(
|
||||
@@ -78,9 +136,11 @@ async function checkInflux() {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
write:append,
|
||||
write: append,
|
||||
writeBatch,
|
||||
query,
|
||||
querySessionHistory,
|
||||
exportSessionCSV,
|
||||
checkInflux
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user