Root cause of the DSCA39 footer mismatch: the "Standard output load for test is 250 ohms." line is a footer note, not a parameter, but the STAGE 1 extractor captured it as a (column-truncated) row "Standard output load for te". And the renderer's OUTSIGTYPE==='CURRENT' emission was wrong on both ends — it printed the note (after the underline, invisible to the validator gate) for many -C current models whose staged originals never had it, and never placed it correctly for the models that do. Fix is data-driven, matching the rest of the template approach: - derive-dsca-templates.js: detect the "Standard output load..." line, capture it as a per-model `loadNote` property, and exclude it from rows. Regenerated dsca-templates.json — surgically clean: only the 5 DSCA39 models changed (lost the truncated row, gained loadNote); all 121 others byte-identical. - datasheet-exact.js: emit `dscaTpl.loadNote` (blank line + note) before the footer underline, only for models that have it; removed the OUTSIGTYPE-based emission. STAGE 3 re-validation: FINAL-TEST CLEAN 85 -> 88, mismatches 9 -> 6, matches 2206 -> 2278. DSCA39-01/02/07 now fully clean; DSCA39-01 byte-content-verified. No regression — the -C current models stayed clean and no longer carry the spurious after-underline note. The 6 remaining dirty models (DSCA38-05/-1793/-19C/-19E, DSCA39-05, DSCA39-1950) are ALL retest data-vintage: the staged .TXT is an older test run than the DB latest-wins record (Supply Current / Linearity differ by more than rounding). Not render bugs — cannot be reconciled against an older sheet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
61 lines
3.8 KiB
JavaScript
61 lines
3.8 KiB
JavaScript
// 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); });
|