/** * Parser for Engineering-Tested SCMHVAS pre-rendered .txt datasheets. * * Source: TS-3R\LOGS\VASLOG\VASLOG - Engineering Tested\*.txt * Each file is a complete, human-readable test datasheet. We extract * metadata for the DB row and keep the full file contents in raw_data * so the export stage can copy it verbatim to X:\For_Web\.TXT. */ const fs = require('fs'); const path = require('path'); // Filename examples: // 166590-1.txt -> SN 166590-1 // 166590-110042023104524.txt -> SN 166590-1, timestamp 10042023104524 // 166594-1010042023090444.txt -> SN 166594-10, timestamp 10042023090444 // The trailing MMDDYYYYhhmmss block (14 digits) is optional and must be // stripped. The SN is the remainder; it always has exactly one dash. // // A single greedy regex can't do this reliably because `\d+-\d+` will // swallow part of the 14-digit timestamp. Split into two steps: // (1) detect and peel the trailing 14-digit timestamp, then // (2) validate what remains as a proper SN (`N-N` optionally followed by // one letter). If the remainder doesn't validate, null the SN so the // in-file `SN:` header wins. const SN_RE = /^\d+-\d+[A-Za-z]?$/; function parseFilename(fileName) { const base = fileName.replace(/\.txt$/i, ''); if (base === fileName) return null; // not a .txt const tsMatch = base.match(/^(.+?)(\d{14})$/); let serialCandidate; let timestamp; if (tsMatch) { serialCandidate = tsMatch[1]; timestamp = tsMatch[2]; } else { serialCandidate = base; timestamp = null; } const serialNumber = SN_RE.test(serialCandidate) ? serialCandidate : null; return { serialNumber, timestamp }; } function extractField(text, label) { const re = new RegExp('^\\s*' + label + ':\\s*(.+?)\\s*$', 'm'); const m = text.match(re); return m ? m[1].trim() : null; } // MM/DD/YYYY or MM-DD-YYYY -> YYYY-MM-DD (DB canonical) function normalizeDate(dateStr) { if (!dateStr) return null; const m = dateStr.match(/^(\d{1,2})[-/](\d{1,2})[-/](\d{4})$/); if (!m) return null; const mm = m[1].padStart(2, '0'); const dd = m[2].padStart(2, '0'); return `${m[3]}-${mm}-${dd}`; } function extractAccuracyStatus(text) { // Line format: " Accuracy 0.007% +/- 0.03% PASS" const m = text.match(/^\s*Accuracy\s+\S+\s+\S+(?:\s+\S+)?\s+(PASS|FAIL)\s*$/mi); return m ? m[1].toUpperCase() : null; } function parseVaslogEngTxt(filePath, testStation = null) { const records = []; try { if (!fs.existsSync(filePath)) return records; const content = fs.readFileSync(filePath, 'utf8'); const baseName = path.basename(filePath); const parsedName = parseFilename(baseName); if (!parsedName) return records; const modelNumber = extractField(content, 'Model'); const dateRaw = extractField(content, 'Date'); const snFromFile = extractField(content, 'SN'); const testDate = normalizeDate(dateRaw); const result = extractAccuracyStatus(content) || 'PASS'; if (!modelNumber || !testDate) return records; // Prefer the in-file SN: header. Fall back to filename-derived SN // only if it validated against SN_RE (parsedName.serialNumber is // null on pathological names, which forces the header to win). const serialNumber = snFromFile || parsedName.serialNumber; if (!serialNumber) return records; records.push({ log_type: 'VASLOG_ENG', model_number: modelNumber.trim(), serial_number: serialNumber.trim(), test_date: testDate, test_station: testStation, overall_result: result, raw_data: content, source_file: filePath, }); } catch (err) { console.error(`Error parsing ${filePath}: ${err.message}`); } return records; } module.exports = { parseVaslogEngTxt, parseFilename };