// Fix 2 STAGE 1 (read-only build): extract per-model DSCA Final-Test templates from staged originals. // Uses the '===' separator line under the Final-Test header to get exact column spans. const fs = require('fs'), path = require('path'); const STAGE = 'C:/Shares/test/STAGE'; const OUT = 'C:/Shares/testdatadb/dsca-templates.json'; function walk(d, out) { let it = []; try { it = fs.readdirSync(d, { withFileTypes: true }); } catch { return out; } for (const e of it) { const p = path.join(d, e.name); if (e.isDirectory()) walk(p, out); else if (/\.txt$/i.test(e.name)) out.push(p); } return out; } function colSpans(sep) { const cols = []; let m; const re = /=+/g; while ((m = re.exec(sep))) cols.push([m.index, m.index + m[0].length]); return cols; } function extract(t) { const lines = t.replace(/\r\n/g, '\n').split('\n'); const accHdr = lines.find(l => /Error \(%\)/.test(l) && /Status/.test(l)) || ''; const accOut = (accHdr.match(/Output \((?:V|mA)\)|Vout \(V\)/) || ['?'])[0]; let fi = lines.findIndex(l => /FINAL TEST RESULTS/.test(l)); if (fi < 0) return null; let hi = -1; for (let i = fi + 1; i < lines.length; i++) { if (/Parameter\s+Measured/.test(lines[i])) { hi = i; break; } } if (hi < 0) return null; const sep = lines[hi + 1] || ''; if (!/=/.test(sep)) return null; const cols = colSpans(sep); if (cols.length < 4) return null; const [pc, mc, sc, stc] = cols; const rows = []; let loadNote = null; for (let i = hi + 2; i < lines.length; i++) { const l = lines[i]; if (/Check List|^\s*_{5,}/.test(l)) break; if (!l.trim()) continue; // The "Standard output load for test is ... ohms." line is a footer note, not // a parameter row — it spans past the name column so column-slicing truncates // it ("Standard output load for te"). Capture the full line as loadNote and // keep it out of rows; the renderer emits it (before the footer underline) // only for models whose staged original actually printed it. if (/^Standard output load/i.test(l.trim())) { loadNote = l.trim(); continue; } const name = (l.slice(pc[0], mc[0]) || '').trim(); const spec = (l.slice(sc[0], stc[0]) || '').trim(); if (!name && !spec) continue; rows.push({ name, spec }); } return { accOut, rows, loadNote }; } (async () => { const files = walk(STAGE, []); const byModel = {}; for (const f of files) { let t; try { t = fs.readFileSync(f, 'utf8'); } catch { continue; } const model = (t.match(/^\s*Model:\s*(\S+)/m) || [])[1] || ''; if (!/^DSCA/i.test(model)) continue; const tpl = extract(t); if (!tpl) continue; // keep the sheet with the MOST rows per model (most complete; avoids truncated samples) if (!byModel[model] || tpl.rows.length > byModel[model].rows.length) byModel[model] = { ...tpl, sheets: (byModel[model] ? byModel[model].sheets : 0) + 1 }; else byModel[model].sheets++; } const models = Object.keys(byModel).sort(); console.log('DSCA models templated: ' + models.length); const out = {}; for (const m of models) { out[m] = { accOut: byModel[m].accOut, rows: byModel[m].rows }; if (byModel[m].loadNote) out[m].loadNote = byModel[m].loadNote; } fs.writeFileSync(OUT, JSON.stringify(out)); console.log('wrote ' + OUT + ' (' + fs.statSync(OUT).size + ' bytes)'); const rc = {}; for (const m of models) { const n = byModel[m].rows.length; rc[n] = (rc[n] || 0) + 1; } console.log('row-count distribution (rows:models): ' + Object.entries(rc).sort((a, b) => a[0] - b[0]).map(([n, c]) => n + ':' + c).join(' ')); for (const probe of ['DSCA38-05', 'DSCA34-01', 'DSCA38-08C', 'DSCA30-01']) { const s = out[probe]; if (s) { console.log('\n' + probe + ' accOut=' + s.accOut + ' rows=' + s.rows.length); s.rows.forEach(r => console.log(' ' + r.name.padEnd(28) + ' | ' + r.spec)); } else console.log('\n' + probe + ': NOT FOUND'); } })().catch(e => { console.error('ERR ' + e.message); });