Files
test_dashboard/server/scanner.js
T

170 lines
6.0 KiB
JavaScript
Raw Normal View History

2026-05-20 11:52:18 -04:00
'use strict';
const fs = require('fs');
const path = require('path');
const readline = require('readline');
const { parseTargetFilename, parseResultFilename, parseTimestamp, parseElapsedTime, parseTputRssi } = require('./parser');
2026-05-20 11:52:18 -04:00
const { upsertTest, markCompleted, clearTests } = require('./db');
/**
* Full rebuild: clears the DB, walks target dir to build the test list,
* then walks results dir to mark completions.
*/
async function fullScan(targetDir, resultsDir) {
clearTests();
if (!targetDir || !resultsDir) return;
console.log(`[scanner] target dir : ${targetDir}`);
console.log(`[scanner] results dir: ${resultsDir}`);
await scanTargets(targetDir);
await scanResults(resultsDir);
}
async function scanTargets(targetDir) {
// Walk the target directory, find all TC_WIFI_*.ini files, parse their names,
// and upsert them into the DB. Ignore files without a valid test_id.
2026-05-20 11:52:18 -04:00
let parentEntries;
try {
parentEntries = fs.readdirSync(targetDir, { withFileTypes: true }).filter(d => d.isDirectory());
} catch (e) {
console.error('[scanner] Cannot read target dir:', e.message);
return;
}
console.log(`[scanner] subdirectories found: ${parentEntries.length}`);
for (const parentEntry of parentEntries) {
const parentDirPath = path.join(targetDir, parentEntry.name);
let files;
try {
files = fs.readdirSync(parentDirPath, { withFileTypes: true })
.filter(f => f.isFile() && !f.name.startsWith('GLOBAL') && f.name.endsWith('.ini'));
2026-05-20 11:52:18 -04:00
} catch (e) {
console.error(`[scanner] Cannot read parent dir ${parentEntry.name}:`, e.message);
continue;
}
console.log(`[scanner] ${parentEntry.name}${files.length} target test file(s)`);
2026-05-20 11:52:18 -04:00
for (const file of files) {
const parsed = parseTargetFilename(file.name, parentEntry.name);
if (!parsed.test_id) {
console.log(`[scanner] skip (no test_id): ${parentEntry.name}/${file.name}`);
2026-05-20 11:52:18 -04:00
continue;
}
console.log(`[scanner] found: ${parentEntry.name}/${file.name} → test_id=${parsed.test_id}`);
2026-05-20 11:52:18 -04:00
upsertTest({
id: parsed.id,
test_id: parsed.test_id,
2026-05-20 11:52:18 -04:00
parent_dir: parentEntry.name,
filename: file.name,
interference: parsed.interference,
device: parsed.device,
rotation: parsed.rotation,
test_point: parsed.test_point,
station: parsed.station,
band: parsed.band,
channel: parsed.channel,
bandwidth: parsed.bandwidth,
rssi: parsed.rssi,
direction: parsed.direction,
});
}
}
}
/**
* Scan the results directory and mark all matched tests as completed.
*/
async function scanResults(resultsDir) {
let entries;
try {
entries = fs.readdirSync(resultsDir, { withFileTypes: true }).filter(d => d.isDirectory());
} catch (e) {
console.error('[scanner] Cannot read results dir:', e.message);
return;
}
console.log(`[scanner] results: ${entries.length} result dir(s) found`);
for (const entry of entries) {
await processResultDir(resultsDir, entry.name);
}
}
/**
* Process one result directory: extract test_id, find the latest matching
2026-05-20 11:52:18 -04:00
* log file, parse its elapsed-time line, and mark the test as completed.
*/
async function processResultDir(resultsDir, resultDirName) {
const { test_id, device } = parseResultFilename(resultDirName);
//console.log(`[scanner] result dir: ${resultDirName} → test_id=${test_id} device=${device}`);
if (!test_id || !device) return;
2026-05-20 11:52:18 -04:00
const resultDirPath = path.join(resultsDir, resultDirName);
let logFiles;
try {
logFiles = fs.readdirSync(resultDirPath, { withFileTypes: true })
.filter(f => f.isFile() && f.name.endsWith('.txt') && f.name.includes(test_id))
2026-05-20 11:52:18 -04:00
.map(f => f.name);
} catch (e) {
console.error(`[scanner] Cannot read result dir ${resultDirName}:`, e.message);
2026-05-20 11:52:18 -04:00
return;
}
if (logFiles.length === 0) {
// Result directory exists but log not written yet — mark complete without timing
console.log(`[scanner] completed (no log yet): ${test_id}`);
markCompleted(test_id, device, null, null);
2026-05-20 11:52:18 -04:00
return;
}
// For re-runs, the latest log file has the latest timestamp in its name
const latestLog = logFiles.sort().at(-1);
const logPath = path.join(resultDirPath, latestLog);
2026-05-20 11:52:18 -04:00
const completed_at = parseTimestamp(latestLog);
const { duration_seconds, tputResults } = await extractLogData(logPath);
2026-05-20 11:52:18 -04:00
console.log(`[scanner] completed: ${test_id} device=${device} duration=${duration_seconds}s stations=${tputResults.length} at=${completed_at}`);
markCompleted(test_id, device, completed_at, duration_seconds, tputResults);
2026-05-20 11:52:18 -04:00
}
/**
* Single-pass log file reader: extracts elapsed time and all station tput/rssi entries.
2026-05-20 11:52:18 -04:00
*/
function extractLogData(logFilePath) {
const ELAPSED_REGEX = /\[.*?INFO\]\s+Elapsed time\s*:\s*([\d]+:[\d]{2}:[\d]{2}\.[\d]+)/;
const TPUT_RSSI_REGEX = /\[.*?INFO\]\s+STA(\d+) over angles >> AVG IxChariot TPUT: (\d+) Mbps, AVG DL RSSI: (-?\d+) dBm, AVG UL RSSI: (-?\d+) dBm/
2026-05-20 11:52:18 -04:00
return new Promise((resolve) => {
const result = { duration_seconds: null, tputResults: [] };
2026-05-20 11:52:18 -04:00
let stream;
try {
stream = fs.createReadStream(logFilePath, { encoding: 'utf8' });
} catch (e) {
console.error(`[scanner] Cannot read log file ${logFilePath}:`, e.message);
return resolve(result);
2026-05-20 11:52:18 -04:00
}
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
rl.on('line', (line) => {
const timeMatch = line.match(ELAPSED_REGEX);
const tputMatch = line.match(TPUT_RSSI_REGEX);
if (timeMatch) result.duration_seconds = parseElapsedTime(timeMatch[1]);
else if (tputMatch) {
parsed = parseTputRssi(line);
if (parsed) result.tputResults.push(parsed);
2026-05-20 11:52:18 -04:00
}
});
rl.on('close', () => resolve(result));
rl.on('error', () => resolve(result));
stream.on('error', () => resolve(result));
2026-05-20 11:52:18 -04:00
});
}
module.exports = { fullScan, scanResults, processResultDir };