Files
test_dashboard/server/watcher.js
T
Mia.Wu a67815c61a feat: refactor parsing and scanning logic for test files
- Updated `parseFilename` to `parseTargetFilename` and modified its return structure to include `test_id` instead of `file_id`.
- Introduced `parseResultFilename` to extract `test_id` and `device` from result file names.
- Enhanced `fullScan` to separately handle target and results directories, improving clarity and functionality.
- Updated database interactions to use `test_id` instead of `file_id` across various modules.
- Added a new `/rescan` endpoint to trigger a full scan of target and results directories.
- Improved logging and error handling throughout the scanning process.
- Introduced `parseTputRssi` to extract throughput and RSSI data from log files.
2026-05-21 14:53:08 -04:00

86 lines
3.1 KiB
JavaScript

'use strict';
const chokidar = require('chokidar');
const path = require('path');
const { fullScan, processResultDir } = require('./scanner');
const { resetByFileIdAndDevice } = require('./db');
const { broadcast } = require('./sse');
let targetWatcher = null;
let resultsWatcher = null;
function debounce(fn, ms) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}
function startWatching(targetDir, resultsDir) {
stopWatching();
if (!targetDir || !resultsDir) return;
// ── Target dir ────────────────────────────────────────────────────────────
// Any change to test files (new cases added or removed) triggers a full
// rescan so the test list stays in sync.
targetWatcher = chokidar.watch(targetDir, {
depth: 1,
ignoreInitial: true,
awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 },
});
const debouncedFullScan = debounce(async () => {
await fullScan(targetDir, resultsDir);
broadcast({ type: 'update' });
}, 1000);
targetWatcher.on('add', debouncedFullScan);
targetWatcher.on('unlink', debouncedFullScan);
// ── Results dir ───────────────────────────────────────────────────────────
// depth: 1 covers both immediate result subdirectories (addDir/unlinkDir)
// and log files inside them (add).
resultsWatcher = chokidar.watch(resultsDir, {
depth: 1,
ignoreInitial: true,
awaitWriteFinish: { stabilityThreshold: 2000, pollInterval: 200 },
});
// New result directory → mark test complete
resultsWatcher.on('addDir', async (dirPath) => {
if (path.normalize(dirPath) === path.normalize(resultsDir)) return;
const dirName = path.basename(dirPath);
await processResultDir(resultsDir, dirName);
broadcast({ type: 'update' });
});
// New log file inside a result directory → update timing
resultsWatcher.on('add', async (filePath) => {
const dirPath = path.dirname(filePath);
if (path.normalize(dirPath) === path.normalize(resultsDir)) return;
const dirName = path.basename(dirPath);
await processResultDir(resultsDir, dirName);
broadcast({ type: 'update' });
});
// Result directory deleted → reset test to pending
resultsWatcher.on('unlinkDir', (dirPath) => {
if (path.normalize(dirPath) === path.normalize(resultsDir)) return;
const dirName = path.basename(dirPath);
const segs = dirName.split(/[_\-]/);
const test_id = segs.find(s => /^R\d+[A-Z0-9]+$/i.test(s)) || null;
const device = segs.find(s => /^CGW\d+$/i.test(s)) || null;
if (test_id) {
resetByFileIdAndDevice(test_id, device);
broadcast({ type: 'update' });
}
});
}
function stopWatching() {
if (targetWatcher) { targetWatcher.close(); targetWatcher = null; }
if (resultsWatcher) { resultsWatcher.close(); resultsWatcher = null; }
}
module.exports = { startWatching, stopWatching };