Add SCMVAS/SCMHVAS datasheet pipeline extension (Dataforth)
Extends the Test Datasheet Pipeline on AD2:C:\Shares\testdatadb to
generate web-published datasheets for the SCMVAS-Mxxx (obsolete) and
SCMHVAS-Mxxxx (replacement) High Voltage Input Module product lines.
Both are tested either with the existing TESTHV3 software (production
VASLOG .DAT logs) or in Engineering with plain .txt output.
Key changes on AD2 (all deployed 2026-04-12 with dated backups):
- parsers/spec-reader.js: getSpecs() returns a `{_family:'SCMVAS',
_noSpecs:true}` sentinel for SCMVAS/SCMHVAS/VAS-M/HVAS-M model prefixes
so the export pipeline does not silently skip them for missing specs.
- templates/datasheet-exact.js: new Accuracy-only template branch
(generateSCMVASDatasheet + helpers) that mirrors the existing shipped
format byte-for-byte. Extraction regex covers both QuickBASIC STR$()
output formats: scientific-with-trailing-status-digit (98.4% of
records) and plain-decimal (1.6% of records above QB's threshold).
- parsers/vaslog-engtxt.js (new): parses the Engineering-Tested .txt
files in TS-3R\LOGS\VASLOG\VASLOG - Engineering Tested\. Filename SN
regex strips optional trailing 14-digit timestamp; in-file "SN:"
header is the authoritative source when the filename is malformed.
- database/import.js: LOG_TYPES grows a VASLOG_ENG entry with
subfolder + recursive flags. Pre-existing 7 log types keep their
implicit recursive=true behaviour (config.recursive !== false).
importFiles() routes VASLOG_ENG paths before the generic loop so a
VASLOG - Engineering Tested/*.txt path does not mis-dispatch to the
multiline parser.
- database/export-datasheets.js: VASLOG_ENG records are written
verbatim via fs.copyFileSync(source_file, For_Web/<SN>.TXT) for true
byte-level pass-through, with a graceful raw_data fallback when the
source file is no longer on disk.
Deploy outcome:
- 27,503 SCMVAS/SCMHVAS datasheets rendered (27,065 from scientific +
438 from plain-decimal PASS lines, post-patch rerun)
- 434 Engineering-Tested .txt files pass-through-copied to For_Web
- 0 errors across both batches
Repo layout added here:
- scmvas-hvas-research/: discovery artifacts (source .BAS, hvin.dat,
sample .DAT + .txt, binary-format notes, IMPLEMENTATION_PLAN.md)
- implementation/: staged final code + deploy helpers + local test
harness + per-step verification scripts
- backups/pre-deploy-20260412/: independent local snapshot of the 4
AD2 files replaced, pulled byte-for-byte before deploy
All helper scripts fetch the AD2 password at runtime from the SOPS
vault (clients/dataforth/ad2.sops.yaml). None of the committed files
contain the plaintext credential. Known vault-entry hygiene issue
(stale shell-escape backslash before the `!`) is documented in the
fetcher comments and stripped at read-time; flagged separately for
cleanup.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
# SCMVAS/SCMHVAS Datasheet Pipeline Integration — Implementation Plan
|
||||
|
||||
**Created:** 2026-04-12
|
||||
**Basis:** Discovery + sample analysis completed 2026-04-11
|
||||
**Target environment:** AD2 server, `C:\Shares\testdatadb\`
|
||||
**Decision:** Option C — simple Accuracy-only datasheet, generated directly from DB record, no `hvin.dat` lookup needed
|
||||
|
||||
---
|
||||
|
||||
## Scope
|
||||
|
||||
Two product families need first-class support in the automated datasheet pipeline:
|
||||
|
||||
- **SCMVAS-Mxxx** — obsolete, datasheets end ~2024 plus occasional retests
|
||||
- **SCMHVAS-Mxxxx** — replacement line (two test paths):
|
||||
- Production half → TESTHV3 software → logs at `TS-3R\LOGS\VASLOG\*.DAT` (multiline CSV format, same as 5BLOG/8BLOG)
|
||||
- Engineering half → plain `.txt` output pre-rendered at `TS-3R\LOGS\VASLOG\VASLOG - Engineering Tested\*.txt`
|
||||
|
||||
### Sample datasheet format (the exact output we must produce)
|
||||
|
||||
```
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 10/04/2023
|
||||
Model: SCMHVAS-M0200
|
||||
SN: 166590-1
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.007% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
```
|
||||
|
||||
Each line prefixed with 8 spaces. Tilde separator line is 71 tildes. Specification string is constant `+/- 0.03%`. Check List uses `__X__` markers (pre-filled, not blank like SCM7B).
|
||||
|
||||
---
|
||||
|
||||
## Accuracy extraction rule (production VASLOG .DAT)
|
||||
|
||||
The raw_data PASS/FAIL line looks like `"PASS-7.005501E-033"` or `"PASS 5.999184E-033"`. Format is:
|
||||
|
||||
```
|
||||
<"PASS"|"FAIL"> <optional space> <signed-float-7digits> E - <2-digit-exponent> <trailing-status-digit>
|
||||
```
|
||||
|
||||
The trailing single digit (observed: `2` or `3`) is a test-status code, NOT part of the float. The captured float is **already in percent units** (not a fraction).
|
||||
|
||||
**Extraction regex:** `/^(PASS|FAIL)\s*(-?\d+\.?\d*E[+-]?\d{2})\d?$/i` applied to the stripped contents of the quoted status string.
|
||||
|
||||
**Formatting:** abs value, 3 decimals, trim trailing zeros → display like `0.007%`, `0.01%`, `0.005%`.
|
||||
|
||||
Verified against samples:
|
||||
- `"PASS-7.005501E-033"` → `7.005501E-03` = `0.007005501%` → display `0.007%` ✓
|
||||
- `"PASS 4.988443E-033"` → `4.988443E-03` = `0.004988443%` → display `0.005%` ✓
|
||||
- `"PASS 1.524978E-023"` → `1.524978E-02` = `0.01524978%` → display `0.015%`
|
||||
|
||||
---
|
||||
|
||||
## Changes by file
|
||||
|
||||
### 1. `parsers/spec-reader.js`
|
||||
|
||||
**Goal:** allow SCMVAS/SCMHVAS model numbers to pass the MODNAME validation filter so they land in the spec map (with a synthetic no-specs stub), OR bypass spec lookup entirely for this family.
|
||||
|
||||
**Approach:** bypass entirely. Cleaner — no stub records.
|
||||
|
||||
Change `getSpecs()` to special-case SCMVAS/SCMHVAS and return a well-known "no-specs" sentinel (e.g. `{ _family: 'SCMVAS', _noSpecs: true }`) instead of `null`. This lets `exportNewRecords()` proceed to formatter without silently skipping.
|
||||
|
||||
Also update the MODNAME prefix regex at line 287 (`^(SCM5B|5B|SCM7B|7B|8B|DSCA|DSCT|SCT|BOGUS)`) — this line rejects records in the binary DAT parser only, which doesn't affect VASLOG (VASLOG isn't read through that code path). No change needed here — leaving SCMVAS/SCMHVAS out of the binary parser filter is correct since we don't parse `hvin.dat`.
|
||||
|
||||
**Diff scope:** ~20 lines in `getSpecs()`.
|
||||
|
||||
### 2. `templates/datasheet-exact.js`
|
||||
|
||||
**Goal:** new family branch emitting the simple Accuracy-only template.
|
||||
|
||||
**Approach:** Add `SCMVAS` to DATA_LINES (single-entry array: `[['Accuracy', '%']]`). At the top of `generateExactDatasheet()`, if the spec stub flags `_family === 'SCMVAS'`, route to a dedicated `generateSCMVASDatasheet(record)` helper that builds the 35-line template above. This helper does NOT use `specs` — only `record.model_number`, `record.serial_number`, `record.test_date`, `record.overall_result`, and `record.raw_data`.
|
||||
|
||||
The helper must:
|
||||
- Render 8-space left indent on every line
|
||||
- Date formatted `MM/DD/YYYY` (matching newer samples) — note: "Corrected HVAS" uses `MM-DD-YYYY`; use `MM/DD/YYYY` per the most recent Engineering-Tested samples
|
||||
- Extract accuracy value via the regex above
|
||||
- Constants: specification = `+/- 0.03%`, withstand/Hi-Pot block omitted (SCMVAS has none), checklist uses `__X__` markers
|
||||
|
||||
Also delete the vestigial `startsWith('SCMHVAS')` check at existing line 652 (it was inside the DSCT branch and is no longer reachable once SCMVAS gets its own branch).
|
||||
|
||||
**Diff scope:** ~80 new lines (new helper + DATA_LINES entry + router change + one deletion).
|
||||
|
||||
### 3. `database/export-datasheets.js`
|
||||
|
||||
**Goal:** do not skip SCMVAS/SCMHVAS records due to missing specs.
|
||||
|
||||
**Approach:** after changing `getSpecs()` to return a stub for this family, the existing `if (!specs) continue;` logic in both `run()` and `exportNewRecords()` just works. No explicit change needed — verify only.
|
||||
|
||||
### 4. `database/import.js`
|
||||
|
||||
**Goal:** ingest the Engineering-Tested plain `.txt` files.
|
||||
|
||||
**Approach:** Add a new dedicated import branch for the `VASLOG - Engineering Tested` subfolder:
|
||||
|
||||
- Add a new parser `parsers/vaslog-engtxt.js` that:
|
||||
- Takes a `.txt` filepath, parses SN from filename (pattern `^(\d+-\d+[A-Za-z]?)(?:\d{14})?\.txt$`, capturing the SN segment like `166590-1` or `167601-4` — the optional 14-digit timestamp suffix `MMDDYYYYhhmmss` is dropped)
|
||||
- Reads the file, extracts `Model:`, `Date:`, `SN:`, `Accuracy`, and `Status` from the plain-text header rows
|
||||
- Returns one record with `log_type='VASLOG_ENG'`, `overall_result='PASS'` (or derive from the Status field), `raw_data=<full file contents>`, `source_file=<path>`
|
||||
- Register `VASLOG_ENG` in the LOG_TYPES map with a new `vaslog-engtxt` parser alias
|
||||
- Make the `importStationLogs()` walk recurse into `VASLOG/` one level to pick up the `VASLOG - Engineering Tested/*.txt` subfolder. Cleanest: parameterize the LOG_TYPES entry with `subfolder` and `recursive` flags.
|
||||
|
||||
For the **pass-through copy to `X:\For_Web\<SN>.TXT`**, the Engineering-Tested files already have the correct final format. Two sub-options:
|
||||
|
||||
- **(4a) Pass-through**: `exportNewRecords()` detects `log_type === 'VASLOG_ENG'`, copies `raw_data` (the original file contents) verbatim into `X:\For_Web\<SN>.TXT`, sets `forweb_exported_at`. Zero risk of format drift.
|
||||
- **(4b) Re-render**: treat the `VASLOG_ENG` record the same as a VASLOG record — run it through the same `generateSCMVASDatasheet()` helper. Consistent with production path.
|
||||
|
||||
**Recommendation: (4a) pass-through.** Reasons:
|
||||
- The files already match the target format exactly (verified by comparing samples to `Corrected HVAS Files/*.txt`)
|
||||
- Preserves any Engineering-hand-tweaked formatting
|
||||
- If drift is ever needed, switching to (4b) is a one-line change later
|
||||
|
||||
**Diff scope:** new `parsers/vaslog-engtxt.js` (~60 lines) + ~30 lines across `import.js` + ~15 lines in `export-datasheets.js` for the pass-through branch.
|
||||
|
||||
### 5. `C:\Shares\test\scripts\Sync-FromNAS-rsync.ps1`
|
||||
|
||||
**Goal:** ensure the Engineering-Tested subfolder is included in the sync.
|
||||
|
||||
**Approach:** Verify that the existing `TS-3R\LOGS\` rsync already pulls the full subtree (`--recursive`). Based on prior session logs, the rsync syncs `TS-3R/*`, so the subfolder likely rides along. **Verify only — no change expected.** If rsync uses explicit includes, add `VASLOG - Engineering Tested/***` to the include list.
|
||||
|
||||
### 6. Database schema
|
||||
|
||||
**Goal:** no schema changes required.
|
||||
|
||||
The `test_records` table already has `log_type`, `model_number`, `serial_number`, `test_date`, `overall_result`, `raw_data`, `source_file`, `forweb_exported_at`. The new `VASLOG_ENG` log_type is just a new string value in an existing column.
|
||||
|
||||
### 7. Backfill strategy
|
||||
|
||||
**Production VASLOG .DAT**: these are already imported into `test_records` via the existing multiline parser. After the spec-reader/formatter changes deploy, run:
|
||||
|
||||
```
|
||||
node database/export-datasheets.js --limit 0
|
||||
```
|
||||
|
||||
to regenerate datasheets for all PASS SCMVAS/SCMHVAS records where `forweb_exported_at IS NULL`. This backfills historical SCMVAS/SCMHVAS records that were previously skipped due to "no specs".
|
||||
|
||||
**Engineering-Tested .txt**: run the full import once after the new parser is added. Should pick up all 434 existing files and copy them to `X:\For_Web\`.
|
||||
|
||||
---
|
||||
|
||||
## Risks / edge cases
|
||||
|
||||
1. **The `VAS-MPT.DAT` / `HVAS-MPT.DAT` "pass-through" models** — might need a slightly different treatment (skip Check List? different wording?). Treat same as regular SCMVAS for now; revisit if user reports a mismatch.
|
||||
2. **FAIL records** — the PASS regex above also matches `FAIL`. Verify the Status column in the output shows `FAIL` and that the existing `exportNewRecords` logic (which filters `overall_result = 'PASS'`) skips FAIL datasheets by default. No action needed.
|
||||
3. **Filename SN extraction for Engineering-Tested** — observed patterns: `166590-1.txt`, `166590-110042023104524.txt` (trailing timestamp). Regex must correctly split the timestamp. A small number of edge cases exist (e.g. `166594-1010042023090444.txt` = SN `166594-10`, timestamp `10042023090444`) — the SN has variable-length second segment. Safe rule: SN ends at the last `-<digits>` segment before the optional 14-digit timestamp.
|
||||
4. **Duplicate files** — `166593-4.txt` (1519 bytes) and `166593-410042023114928.txt` (1600 bytes) coexist. Treat the timestamped filename as canonical; untimestamped is a later re-render. Import both but dedupe on `(log_type, model_number, serial_number, test_date, test_station)` (existing unique constraint already handles this).
|
||||
5. **Date format variance** — production VASLOG stores `MM-DD-YYYY` in raw_data; Engineering-Tested `.txt` uses `MM/DD/YYYY` or `MM-DD-YYYY` depending on vintage. Normalize all date displays to `MM/DD/YYYY` per the newest Engineering-Tested output.
|
||||
|
||||
---
|
||||
|
||||
## Test plan (Coding Agent must verify)
|
||||
|
||||
Before declaring complete:
|
||||
|
||||
1. `node database/export-datasheets.js --dry-run --serial 179379-1` → should preview a well-formed SCMHVAS datasheet (no "missing specs" skip).
|
||||
2. `node database/export-datasheets.js --serial 166590-1` → compare generated `X:\For_Web\166590-1.TXT` byte-for-byte against the existing `samples/vaslog-engtxt/166590-110042023104524.txt`. Expect visual match; char-level drift acceptable only in whitespace.
|
||||
3. Full incremental import of the `VASLOG - Engineering Tested` subfolder → verify all 434 `.txt` files copy to `X:\For_Web\`.
|
||||
4. Historical backfill of production VASLOG records → spot-check 5 SCMHVAS and 5 SCMVAS datasheets against any known-good reference in `Corrected HVAS Files/`.
|
||||
5. Regression: pick 10 existing SCM5B + 10 DSCA datasheets, regenerate, confirm no format drift vs. their current `X:\For_Web\*.TXT`.
|
||||
|
||||
---
|
||||
|
||||
## Delegation
|
||||
|
||||
Once this plan is approved, hand off to the Coding Agent with:
|
||||
- This plan as the spec
|
||||
- All research artifacts under `projects/dataforth-dos/datasheet-pipeline/scmvas-hvas-research/`
|
||||
- Sample output at `samples/corrected-hvas/171087-1.txt` and `samples/vaslog-engtxt/166590-110042023104524.txt` as golden references
|
||||
- Access to AD2 via `paramiko` (creds from vault path `clients/dataforth/ad2.sops.yaml`)
|
||||
|
||||
After implementation, mandatory Code Review Agent pass before deploying to `C:\Shares\testdatadb\`.
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Archive For_Web Files
|
||||
*
|
||||
* Moves files older than the current year into year-based subfolders.
|
||||
* e.g., X:\For_Web\2024\12345-1.TXT
|
||||
*
|
||||
* The TestDataSheetUploader only uploads files modified in the current year,
|
||||
* so archived files won't be re-uploaded. Keeps the active folder small and fast.
|
||||
*
|
||||
* Usage:
|
||||
* node archive-for-web.js Archive all pre-current-year files
|
||||
* node archive-for-web.js --dry-run Show what would be moved
|
||||
* node archive-for-web.js --year 2024 Only archive files from 2024
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const FOR_WEB = 'X:\\For_Web';
|
||||
|
||||
function run() {
|
||||
const args = process.argv.slice(2);
|
||||
const dryRun = args.includes('--dry-run');
|
||||
const yearIdx = args.indexOf('--year');
|
||||
const targetYear = yearIdx >= 0 ? parseInt(args[yearIdx + 1]) : null;
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
console.log('========================================');
|
||||
console.log('Archive For_Web Files');
|
||||
console.log('========================================');
|
||||
console.log(`Source: ${FOR_WEB}`);
|
||||
console.log(`Current year: ${currentYear}`);
|
||||
console.log(`Dry run: ${dryRun}`);
|
||||
if (targetYear) console.log(`Target year: ${targetYear}`);
|
||||
console.log(`Start: ${new Date().toISOString()}`);
|
||||
console.log('');
|
||||
|
||||
if (!fs.existsSync(FOR_WEB)) {
|
||||
console.error('ERROR: For_Web directory not found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Scan files
|
||||
console.log('Scanning files...');
|
||||
const entries = fs.readdirSync(FOR_WEB, { withFileTypes: true });
|
||||
|
||||
const yearCounts = {};
|
||||
let scanned = 0;
|
||||
let toMove = 0;
|
||||
let moved = 0;
|
||||
let errors = 0;
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!entry.isFile()) continue;
|
||||
scanned++;
|
||||
|
||||
if (scanned % 50000 === 0) {
|
||||
process.stdout.write(`\rScanned: ${scanned}`);
|
||||
}
|
||||
|
||||
const filePath = path.join(FOR_WEB, entry.name);
|
||||
let stat;
|
||||
try {
|
||||
stat = fs.statSync(filePath);
|
||||
} catch (err) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fileYear = stat.mtime.getFullYear();
|
||||
|
||||
// Skip current year files
|
||||
if (fileYear >= currentYear) continue;
|
||||
|
||||
// If targeting a specific year, skip others
|
||||
if (targetYear && fileYear !== targetYear) continue;
|
||||
|
||||
yearCounts[fileYear] = (yearCounts[fileYear] || 0) + 1;
|
||||
toMove++;
|
||||
|
||||
if (!dryRun) {
|
||||
// Create year subdirectory if needed
|
||||
const yearDir = path.join(FOR_WEB, String(fileYear));
|
||||
if (!fs.existsSync(yearDir)) {
|
||||
fs.mkdirSync(yearDir);
|
||||
console.log(`\nCreated directory: ${yearDir}`);
|
||||
}
|
||||
|
||||
const destPath = path.join(yearDir, entry.name);
|
||||
try {
|
||||
fs.renameSync(filePath, destPath);
|
||||
moved++;
|
||||
} catch (err) {
|
||||
// If rename fails (cross-device), try copy+delete
|
||||
try {
|
||||
fs.copyFileSync(filePath, destPath);
|
||||
fs.unlinkSync(filePath);
|
||||
moved++;
|
||||
} catch (err2) {
|
||||
console.error(`\nERROR moving ${entry.name}: ${err2.message}`);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
if (moved % 10000 === 0) {
|
||||
process.stdout.write(`\rMoved: ${moved}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n');
|
||||
console.log('========================================');
|
||||
console.log('Archive Summary');
|
||||
console.log('========================================');
|
||||
console.log(`Files scanned: ${scanned}`);
|
||||
console.log(`Files to archive: ${toMove}`);
|
||||
|
||||
if (Object.keys(yearCounts).length > 0) {
|
||||
console.log('\nBy year:');
|
||||
for (const [year, count] of Object.entries(yearCounts).sort()) {
|
||||
console.log(` ${year}: ${count.toLocaleString()} files`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dryRun) {
|
||||
console.log(`\nFiles moved: ${moved}`);
|
||||
console.log(`Errors: ${errors}`);
|
||||
}
|
||||
|
||||
console.log(`\nEnd: ${new Date().toISOString()}`);
|
||||
}
|
||||
|
||||
run();
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* PostgreSQL Database Abstraction Layer
|
||||
*
|
||||
* Provides a connection pool and helper methods for the TestDataDB app.
|
||||
* Replaces better-sqlite3 singleton with pg.Pool.
|
||||
*
|
||||
* Environment variables (all optional, defaults connect to local PG):
|
||||
* PGHOST (default: localhost)
|
||||
* PGPORT (default: 5432)
|
||||
* PGUSER (default: testdatadb_app)
|
||||
* PGPASSWORD (default: DfTestDB2026!)
|
||||
* PGDATABASE (default: testdatadb)
|
||||
*/
|
||||
|
||||
const { Pool } = require('pg');
|
||||
|
||||
const pool = new Pool({
|
||||
host: process.env.PGHOST || 'localhost',
|
||||
port: parseInt(process.env.PGPORT || '5432', 10),
|
||||
user: process.env.PGUSER || 'testdatadb_app',
|
||||
password: process.env.PGPASSWORD || 'DfTestDB2026!',
|
||||
database: process.env.PGDATABASE || 'testdatadb',
|
||||
max: 20,
|
||||
idleTimeoutMillis: 30000,
|
||||
connectionTimeoutMillis: 5000,
|
||||
});
|
||||
|
||||
pool.on('error', (err) => {
|
||||
console.error(`[${new Date().toISOString()}] [PG POOL ERROR] ${err.message}`);
|
||||
});
|
||||
|
||||
/**
|
||||
* Convert SQLite-style ? placeholders to PostgreSQL $1, $2, ... placeholders.
|
||||
* Skips ? inside single-quoted strings.
|
||||
*/
|
||||
function convertPlaceholders(sql) {
|
||||
let idx = 0;
|
||||
let inString = false;
|
||||
let result = '';
|
||||
for (let i = 0; i < sql.length; i++) {
|
||||
const ch = sql[i];
|
||||
if (ch === "'" && (i === 0 || sql[i - 1] !== '\\')) {
|
||||
inString = !inString;
|
||||
result += ch;
|
||||
} else if (ch === '?' && !inString) {
|
||||
idx++;
|
||||
result += '$' + idx;
|
||||
} else {
|
||||
result += ch;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a query, return all rows.
|
||||
* @param {string} sql - SQL with ? or $N placeholders
|
||||
* @param {Array} params - Parameter values
|
||||
* @returns {Promise<Array>} rows
|
||||
*/
|
||||
async function query(sql, params = []) {
|
||||
const pgSql = sql.includes('?') ? convertPlaceholders(sql) : sql;
|
||||
const result = await pool.query(pgSql, params);
|
||||
return result.rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a query, return the first row or null.
|
||||
*/
|
||||
async function queryOne(sql, params = []) {
|
||||
const rows = await query(sql, params);
|
||||
return rows[0] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a statement (INSERT/UPDATE/DELETE), return { rowCount }.
|
||||
*/
|
||||
async function execute(sql, params = []) {
|
||||
const pgSql = sql.includes('?') ? convertPlaceholders(sql) : sql;
|
||||
const result = await pool.query(pgSql, params);
|
||||
return { rowCount: result.rowCount, rows: result.rows };
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a function inside a transaction.
|
||||
* The callback receives a client with query/execute helpers.
|
||||
* @param {Function} fn - async (client) => result
|
||||
* @returns {Promise<*>} result of fn
|
||||
*/
|
||||
async function transaction(fn) {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
|
||||
const txClient = {
|
||||
async query(sql, params = []) {
|
||||
const pgSql = sql.includes('?') ? convertPlaceholders(sql) : sql;
|
||||
const result = await client.query(pgSql, params);
|
||||
return result.rows;
|
||||
},
|
||||
async queryOne(sql, params = []) {
|
||||
const rows = await txClient.query(sql, params);
|
||||
return rows[0] || null;
|
||||
},
|
||||
async execute(sql, params = []) {
|
||||
const pgSql = sql.includes('?') ? convertPlaceholders(sql) : sql;
|
||||
const result = await client.query(pgSql, params);
|
||||
return { rowCount: result.rowCount, rows: result.rows };
|
||||
},
|
||||
// Direct pg client access for COPY or other advanced operations
|
||||
raw: client,
|
||||
};
|
||||
|
||||
const result = await fn(txClient);
|
||||
await client.query('COMMIT');
|
||||
return result;
|
||||
} catch (err) {
|
||||
await client.query('ROLLBACK');
|
||||
throw err;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the pool (for graceful shutdown).
|
||||
*/
|
||||
async function close() {
|
||||
await pool.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw pool (for advanced use like COPY).
|
||||
*/
|
||||
function getPool() {
|
||||
return pool;
|
||||
}
|
||||
|
||||
module.exports = { query, queryOne, execute, transaction, close, getPool, convertPlaceholders };
|
||||
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* Export Datasheets
|
||||
*
|
||||
* Generates TXT datasheets for unexported PASS records and writes them to X:\For_Web\.
|
||||
* Updates forweb_exported_at after successful export.
|
||||
*
|
||||
* Usage:
|
||||
* node export-datasheets.js Export all pending (batch mode)
|
||||
* node export-datasheets.js --limit 100 Export up to 100 records
|
||||
* node export-datasheets.js --file <paths> Export records matching specific source files
|
||||
* node export-datasheets.js --serial 178439-1 Export a specific serial number
|
||||
* node export-datasheets.js --dry-run Show what would be exported without writing
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const db = require('./db');
|
||||
|
||||
const { loadAllSpecs, getSpecs } = require('../parsers/spec-reader');
|
||||
const { generateExactDatasheet } = require('../templates/datasheet-exact');
|
||||
|
||||
// Configuration
|
||||
const OUTPUT_DIR = 'X:\\For_Web';
|
||||
const BATCH_SIZE = 500;
|
||||
|
||||
async function run() {
|
||||
const args = process.argv.slice(2);
|
||||
const dryRun = args.includes('--dry-run');
|
||||
const limitIdx = args.indexOf('--limit');
|
||||
const limit = limitIdx >= 0 ? parseInt(args[limitIdx + 1]) : 0;
|
||||
const serialIdx = args.indexOf('--serial');
|
||||
const serial = serialIdx >= 0 ? args[serialIdx + 1] : null;
|
||||
const fileIdx = args.indexOf('--file');
|
||||
const files = fileIdx >= 0 ? args.slice(fileIdx + 1).filter(f => !f.startsWith('--')) : null;
|
||||
|
||||
console.log('========================================');
|
||||
console.log('Datasheet Export');
|
||||
console.log('========================================');
|
||||
console.log(`Output: ${OUTPUT_DIR}`);
|
||||
console.log(`Dry run: ${dryRun}`);
|
||||
if (limit) console.log(`Limit: ${limit}`);
|
||||
if (serial) console.log(`Serial: ${serial}`);
|
||||
console.log(`Start: ${new Date().toISOString()}`);
|
||||
|
||||
if (!dryRun && !fs.existsSync(OUTPUT_DIR)) {
|
||||
console.error(`ERROR: Output directory does not exist: ${OUTPUT_DIR}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('\nLoading model specs...');
|
||||
const specMap = loadAllSpecs();
|
||||
|
||||
// Build query
|
||||
const conditions = [`overall_result = 'PASS'`, `forweb_exported_at IS NULL`];
|
||||
const params = [];
|
||||
let paramIdx = 0;
|
||||
|
||||
if (serial) {
|
||||
paramIdx++;
|
||||
conditions.push(`serial_number = $${paramIdx}`);
|
||||
params.push(serial);
|
||||
}
|
||||
|
||||
if (files && files.length > 0) {
|
||||
const placeholders = files.map(() => { paramIdx++; return `$${paramIdx}`; }).join(',');
|
||||
conditions.push(`source_file IN (${placeholders})`);
|
||||
params.push(...files);
|
||||
}
|
||||
|
||||
let sql = `SELECT * FROM test_records WHERE ${conditions.join(' AND ')} ORDER BY test_date DESC`;
|
||||
|
||||
if (limit) {
|
||||
paramIdx++;
|
||||
sql += ` LIMIT $${paramIdx}`;
|
||||
params.push(limit);
|
||||
}
|
||||
|
||||
const records = await db.query(sql, params);
|
||||
console.log(`\nFound ${records.length} records to export`);
|
||||
|
||||
if (records.length === 0) {
|
||||
console.log('Nothing to export.');
|
||||
await db.close();
|
||||
return { exported: 0, skipped: 0, errors: 0 };
|
||||
}
|
||||
|
||||
let exported = 0;
|
||||
let skipped = 0;
|
||||
let errors = 0;
|
||||
let noSpecs = 0;
|
||||
let pendingUpdates = [];
|
||||
|
||||
for (const record of records) {
|
||||
try {
|
||||
const specs = getSpecs(specMap, record.model_number);
|
||||
if (!specs) {
|
||||
noSpecs++;
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const txt = generateExactDatasheet(record, specs);
|
||||
if (!txt) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const filename = record.serial_number + '.TXT';
|
||||
const outputPath = path.join(OUTPUT_DIR, filename);
|
||||
|
||||
if (dryRun) {
|
||||
console.log(` [DRY RUN] Would write: ${filename}`);
|
||||
exported++;
|
||||
} else {
|
||||
fs.writeFileSync(outputPath, txt, 'utf8');
|
||||
pendingUpdates.push(record.id);
|
||||
exported++;
|
||||
|
||||
// Batch commit
|
||||
if (pendingUpdates.length >= BATCH_SIZE) {
|
||||
await flushUpdates(pendingUpdates);
|
||||
pendingUpdates = [];
|
||||
process.stdout.write(`\r Exported: ${exported} / ${records.length}`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`\n ERROR exporting ${record.serial_number}: ${err.message}`);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush remaining updates
|
||||
if (pendingUpdates.length > 0) {
|
||||
await flushUpdates(pendingUpdates);
|
||||
}
|
||||
|
||||
console.log(`\n\n========================================`);
|
||||
console.log(`Export Complete`);
|
||||
console.log(`========================================`);
|
||||
console.log(`Exported: ${exported}`);
|
||||
console.log(`Skipped: ${skipped} (${noSpecs} missing specs)`);
|
||||
console.log(`Errors: ${errors}`);
|
||||
console.log(`End: ${new Date().toISOString()}`);
|
||||
|
||||
await db.close();
|
||||
return { exported, skipped, errors };
|
||||
}
|
||||
|
||||
async function flushUpdates(ids) {
|
||||
const now = new Date().toISOString();
|
||||
await db.transaction(async (txClient) => {
|
||||
for (const id of ids) {
|
||||
await txClient.execute(
|
||||
'UPDATE test_records SET forweb_exported_at = $1 WHERE id = $2',
|
||||
[now, id]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Export function for use by import.js (no db argument -- uses shared pool)
|
||||
async function exportNewRecords(specMap, filePaths) {
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
console.log(`[EXPORT] Output directory not found: ${OUTPUT_DIR}`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const conditions = [`overall_result = 'PASS'`, `forweb_exported_at IS NULL`];
|
||||
const params = [];
|
||||
let paramIdx = 0;
|
||||
|
||||
if (filePaths && filePaths.length > 0) {
|
||||
const placeholders = filePaths.map(() => { paramIdx++; return `$${paramIdx}`; }).join(',');
|
||||
conditions.push(`source_file IN (${placeholders})`);
|
||||
params.push(...filePaths);
|
||||
}
|
||||
|
||||
const sql = `SELECT * FROM test_records WHERE ${conditions.join(' AND ')}`;
|
||||
const records = await db.query(sql, params);
|
||||
if (records.length === 0) return 0;
|
||||
|
||||
let exported = 0;
|
||||
|
||||
await db.transaction(async (txClient) => {
|
||||
for (const record of records) {
|
||||
const specs = getSpecs(specMap, record.model_number);
|
||||
if (!specs) continue;
|
||||
|
||||
const txt = generateExactDatasheet(record, specs);
|
||||
if (!txt) continue;
|
||||
|
||||
const filename = record.serial_number + '.TXT';
|
||||
const outputPath = path.join(OUTPUT_DIR, filename);
|
||||
|
||||
try {
|
||||
fs.writeFileSync(outputPath, txt, 'utf8');
|
||||
await txClient.execute(
|
||||
'UPDATE test_records SET forweb_exported_at = $1 WHERE id = $2',
|
||||
[new Date().toISOString(), record.id]
|
||||
);
|
||||
exported++;
|
||||
} catch (err) {
|
||||
console.error(`[EXPORT] Error writing ${filename}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`[EXPORT] Generated ${exported} datasheet(s)`);
|
||||
return exported;
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
run().catch(console.error);
|
||||
}
|
||||
|
||||
module.exports = { exportNewRecords };
|
||||
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* Generate PDF datasheets for specific serial numbers
|
||||
* For Quatronix customer request - 70 datasheets needed urgently
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const Database = require('better-sqlite3');
|
||||
const PDFDocument = require('pdfkit');
|
||||
|
||||
const { loadAllSpecs, getSpecs } = require('../parsers/spec-reader');
|
||||
const { generateExactDatasheet } = require('../templates/datasheet-exact');
|
||||
|
||||
const DB_PATH = path.join(__dirname, 'testdata.db');
|
||||
const OUTPUT_DIR = process.argv[2] || path.join(process.env.USERPROFILE, 'Desktop', 'Quatronix-Datasheets');
|
||||
|
||||
// Build the list of needed serial numbers
|
||||
const needed = [
|
||||
// SCM5B34-03: 177368-6~15
|
||||
...Array.from({length:10}, (_,i) => '177368-' + (i+6)),
|
||||
// SCM5B35-02: 177625-6~10
|
||||
...Array.from({length:5}, (_,i) => '177625-' + (i+6)),
|
||||
// SCM5B38-05: 177963-6
|
||||
'177963-6',
|
||||
// SCM5B392-11: 177199-13
|
||||
'177199-13',
|
||||
// SCM5B40-03: 178444-1
|
||||
'178444-1',
|
||||
// SCM5B41-02: 178362-1
|
||||
'178362-1',
|
||||
// SCM5B42-02: 177299-4, 177299-5
|
||||
'177299-4', '177299-5',
|
||||
// SCM5B45-02D: 178607-1
|
||||
'178607-1',
|
||||
// SCM5B45-04: 178385-4~8
|
||||
...Array.from({length:5}, (_,i) => '178385-' + (i+4)),
|
||||
// SCM5B48-01: 177593-1
|
||||
'177593-1',
|
||||
// SCM5B49-05: 177000-15
|
||||
'177000-15',
|
||||
// DSCA30-05C: 176566-2
|
||||
'176566-2',
|
||||
// DSCA38-19C: 178001-22, 178001-23
|
||||
'178001-22', '178001-23',
|
||||
// DSCA41-02: 178135-2
|
||||
'178135-2',
|
||||
// DSCA38-1468: 178595-1
|
||||
'178595-1',
|
||||
// SCM5B41-02: 177012-1~30
|
||||
...Array.from({length:30}, (_,i) => '177012-' + (i+1)),
|
||||
// SCM5B47S-10: 178768-8
|
||||
'178768-8',
|
||||
// SCM5B45-04D: 177207-4~7
|
||||
...Array.from({length:4}, (_,i) => '177207-' + (i+4)),
|
||||
// 8B51-12: 178601-6~9
|
||||
...Array.from({length:4}, (_,i) => '178601-' + (i+6)),
|
||||
];
|
||||
|
||||
async function generatePdf(txt, outputPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const doc = new PDFDocument({
|
||||
size: 'LETTER',
|
||||
margins: { top: 36, bottom: 36, left: 36, right: 36 }
|
||||
});
|
||||
const stream = fs.createWriteStream(outputPath);
|
||||
stream.on('finish', resolve);
|
||||
stream.on('error', reject);
|
||||
doc.pipe(stream);
|
||||
doc.font('Courier').fontSize(9.5);
|
||||
const lines = txt.split(/\r?\n/);
|
||||
for (const line of lines) {
|
||||
doc.text(line, { lineGap: 1 });
|
||||
}
|
||||
doc.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function run() {
|
||||
console.log('========================================');
|
||||
console.log('Generate Customer PDFs');
|
||||
console.log('========================================');
|
||||
console.log(`Output: ${OUTPUT_DIR}`);
|
||||
console.log(`Serial numbers: ${needed.length}`);
|
||||
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
const specMap = loadAllSpecs();
|
||||
const db = new Database(DB_PATH, { readonly: true });
|
||||
|
||||
let generated = 0;
|
||||
let notFound = [];
|
||||
let noSpecs = [];
|
||||
let errors = [];
|
||||
|
||||
for (const sn of needed) {
|
||||
const record = db.prepare(
|
||||
"SELECT * FROM test_records WHERE serial_number = ? AND overall_result = 'PASS' LIMIT 1"
|
||||
).get(sn);
|
||||
|
||||
if (!record) {
|
||||
notFound.push(sn);
|
||||
continue;
|
||||
}
|
||||
|
||||
const specs = getSpecs(specMap, record.model_number);
|
||||
if (!specs) {
|
||||
noSpecs.push(sn + ' (' + record.model_number + ')');
|
||||
continue;
|
||||
}
|
||||
|
||||
const txt = generateExactDatasheet(record, specs);
|
||||
if (!txt) {
|
||||
errors.push(sn + ' (format failed)');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Write TXT
|
||||
fs.writeFileSync(path.join(OUTPUT_DIR, sn + '.TXT'), txt, 'utf8');
|
||||
|
||||
// Write PDF
|
||||
try {
|
||||
await generatePdf(txt, path.join(OUTPUT_DIR, sn + '.pdf'));
|
||||
generated++;
|
||||
process.stdout.write(`\rGenerated: ${generated}`);
|
||||
} catch (err) {
|
||||
errors.push(sn + ' (PDF: ' + err.message + ')');
|
||||
}
|
||||
}
|
||||
|
||||
db.close();
|
||||
|
||||
console.log('\n\n========================================');
|
||||
console.log('Results');
|
||||
console.log('========================================');
|
||||
console.log(`Generated: ${generated} (TXT + PDF)`);
|
||||
if (notFound.length > 0) {
|
||||
console.log(`\nNot in database (${notFound.length}):`);
|
||||
notFound.forEach(s => console.log(' ' + s));
|
||||
}
|
||||
if (noSpecs.length > 0) {
|
||||
console.log(`\nNo spec data (${noSpecs.length}):`);
|
||||
noSpecs.forEach(s => console.log(' ' + s));
|
||||
}
|
||||
if (errors.length > 0) {
|
||||
console.log(`\nErrors (${errors.length}):`);
|
||||
errors.forEach(s => console.log(' ' + s));
|
||||
}
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
||||
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* Work Order Report Importer
|
||||
*
|
||||
* Imports work order status reports from TS-XX/Reports/ into PostgreSQL.
|
||||
* Links work order numbers to existing test records.
|
||||
*
|
||||
* Usage:
|
||||
* node import-work-orders.js Full import from all stations
|
||||
* node import-work-orders.js --file <paths> Import specific report files
|
||||
* node import-work-orders.js --station TS-4L Import from one station
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const db = require('./db');
|
||||
const { parseWoReport } = require('../parsers/wo-report');
|
||||
|
||||
const TEST_PATH = 'C:\\Shares\\test';
|
||||
|
||||
async function run() {
|
||||
const args = process.argv.slice(2);
|
||||
const stationIdx = args.indexOf('--station');
|
||||
const targetStation = stationIdx >= 0 ? args[stationIdx + 1] : null;
|
||||
const fileIdx = args.indexOf('--file');
|
||||
const specificFiles = fileIdx >= 0 ? args.slice(fileIdx + 1).filter(f => !f.startsWith('--')) : null;
|
||||
|
||||
console.log('========================================');
|
||||
console.log('Work Order Report Import');
|
||||
console.log('========================================');
|
||||
console.log(`Start: ${new Date().toISOString()}`);
|
||||
|
||||
let files = [];
|
||||
|
||||
if (specificFiles && specificFiles.length > 0) {
|
||||
files = specificFiles;
|
||||
} else {
|
||||
try {
|
||||
const stationDirs = fs.readdirSync(TEST_PATH, { withFileTypes: true })
|
||||
.filter(d => d.isDirectory() && d.name.match(/^TS-/i))
|
||||
.filter(d => !targetStation || d.name.toUpperCase() === targetStation.toUpperCase())
|
||||
.map(d => d.name);
|
||||
|
||||
for (const station of stationDirs) {
|
||||
const reportsDir = path.join(TEST_PATH, station, 'Reports');
|
||||
if (!fs.existsSync(reportsDir)) continue;
|
||||
|
||||
const reportFiles = fs.readdirSync(reportsDir)
|
||||
.filter(f => f.toUpperCase().endsWith('.TXT'))
|
||||
.map(f => path.join(reportsDir, f));
|
||||
|
||||
files.push(...reportFiles);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error scanning stations:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Found ${files.length} report files to import`);
|
||||
|
||||
let woCount = 0;
|
||||
let lineCount = 0;
|
||||
let linkedCount = 0;
|
||||
let errors = 0;
|
||||
|
||||
const BATCH_SIZE = 500;
|
||||
let batch = [];
|
||||
|
||||
for (const filePath of files) {
|
||||
try {
|
||||
const wo = parseWoReport(filePath);
|
||||
if (!wo.wo_number) continue;
|
||||
batch.push({ wo, woLines: wo.lines });
|
||||
|
||||
if (batch.length >= BATCH_SIZE) {
|
||||
const result = await processBatch(batch);
|
||||
woCount += result.woCount;
|
||||
lineCount += result.lineCount;
|
||||
linkedCount += result.linkedCount;
|
||||
batch = [];
|
||||
process.stdout.write(`\rProcessed: ${woCount} WOs, ${lineCount} lines`);
|
||||
}
|
||||
} catch (err) {
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush remaining
|
||||
if (batch.length > 0) {
|
||||
const result = await processBatch(batch);
|
||||
woCount += result.woCount;
|
||||
lineCount += result.lineCount;
|
||||
linkedCount += result.linkedCount;
|
||||
}
|
||||
|
||||
// Bulk update work_order on test_records from serial number pattern
|
||||
console.log('\n\nBulk-linking test records by serial number pattern...');
|
||||
const bulkResult = await db.execute(`
|
||||
UPDATE test_records
|
||||
SET work_order = CASE
|
||||
WHEN serial_number LIKE '%-%'
|
||||
THEN SPLIT_PART(serial_number, '-', 1)
|
||||
ELSE serial_number
|
||||
END
|
||||
WHERE work_order IS NULL
|
||||
`);
|
||||
console.log(`Bulk-linked ${bulkResult.rowCount} test records`);
|
||||
|
||||
console.log('\n========================================');
|
||||
console.log('Import Complete');
|
||||
console.log('========================================');
|
||||
console.log(`Work orders imported: ${woCount}`);
|
||||
console.log(`Test lines imported: ${lineCount}`);
|
||||
console.log(`Test records linked: ${linkedCount}`);
|
||||
console.log(`Errors: ${errors}`);
|
||||
console.log(`End: ${new Date().toISOString()}`);
|
||||
|
||||
await db.close();
|
||||
}
|
||||
|
||||
async function processBatch(items) {
|
||||
let woCount = 0;
|
||||
let lineCount = 0;
|
||||
let linkedCount = 0;
|
||||
|
||||
await db.transaction(async (txClient) => {
|
||||
for (const { wo, woLines } of items) {
|
||||
await txClient.execute(
|
||||
`INSERT INTO work_orders
|
||||
(wo_number, wo_date, program, version, lib_version, test_station, source_file)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
ON CONFLICT (wo_number, test_station)
|
||||
DO UPDATE SET wo_date = EXCLUDED.wo_date, program = EXCLUDED.program,
|
||||
version = EXCLUDED.version, lib_version = EXCLUDED.lib_version,
|
||||
source_file = EXCLUDED.source_file`,
|
||||
[wo.wo_number, wo.wo_date, wo.program, wo.version, wo.lib_version, wo.station, wo.source_file]
|
||||
);
|
||||
woCount++;
|
||||
|
||||
for (const line of woLines) {
|
||||
const result = await txClient.execute(
|
||||
`INSERT INTO work_order_lines
|
||||
(wo_number, serial_number, status, model_number, ds_filename, test_date, test_time, test_station)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
ON CONFLICT (wo_number, serial_number, test_date, test_time) DO NOTHING`,
|
||||
[wo.wo_number, line.serial_number, line.status, line.model_number,
|
||||
line.ds_filename, line.test_date, line.test_time, wo.station]
|
||||
);
|
||||
if (result.rowCount > 0) lineCount++;
|
||||
|
||||
// Link to test_records
|
||||
const linked = await txClient.execute(
|
||||
'UPDATE test_records SET work_order = $1 WHERE serial_number = $2 AND work_order IS NULL',
|
||||
[wo.wo_number, line.serial_number]
|
||||
);
|
||||
if (linked.rowCount > 0) linkedCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { woCount, lineCount, linkedCount };
|
||||
}
|
||||
|
||||
// Export for use by sync script
|
||||
async function importReportFiles(filePaths) {
|
||||
if (!filePaths || filePaths.length === 0) return 0;
|
||||
|
||||
let imported = 0;
|
||||
|
||||
await db.transaction(async (txClient) => {
|
||||
for (const filePath of filePaths) {
|
||||
try {
|
||||
const wo = parseWoReport(filePath);
|
||||
if (!wo.wo_number) continue;
|
||||
|
||||
await txClient.execute(
|
||||
`INSERT INTO work_orders
|
||||
(wo_number, wo_date, program, version, lib_version, test_station, source_file)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
ON CONFLICT (wo_number, test_station)
|
||||
DO UPDATE SET wo_date = EXCLUDED.wo_date, program = EXCLUDED.program,
|
||||
version = EXCLUDED.version, lib_version = EXCLUDED.lib_version,
|
||||
source_file = EXCLUDED.source_file`,
|
||||
[wo.wo_number, wo.wo_date, wo.program, wo.version, wo.lib_version, wo.station, wo.source_file]
|
||||
);
|
||||
|
||||
for (const line of wo.lines) {
|
||||
await txClient.execute(
|
||||
`INSERT INTO work_order_lines
|
||||
(wo_number, serial_number, status, model_number, ds_filename, test_date, test_time, test_station)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
ON CONFLICT (wo_number, serial_number, test_date, test_time) DO NOTHING`,
|
||||
[wo.wo_number, line.serial_number, line.status, line.model_number,
|
||||
line.ds_filename, line.test_date, line.test_time, wo.station]
|
||||
);
|
||||
await txClient.execute(
|
||||
'UPDATE test_records SET work_order = $1 WHERE serial_number = $2 AND work_order IS NULL',
|
||||
[wo.wo_number, line.serial_number]
|
||||
);
|
||||
}
|
||||
imported++;
|
||||
} catch (err) {
|
||||
// skip bad files
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`[WO] Imported ${imported} work order report(s)`);
|
||||
return imported;
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
run().catch(console.error);
|
||||
}
|
||||
|
||||
module.exports = { importReportFiles };
|
||||
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
* Data Import Script
|
||||
* Imports test data from DAT and SHT files into PostgreSQL database
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const db = require('./db');
|
||||
|
||||
const { parseMultilineFile, extractTestStation } = require('../parsers/multiline');
|
||||
const { parseCsvFile } = require('../parsers/csvline');
|
||||
const { parseShtFile } = require('../parsers/shtfile');
|
||||
|
||||
// Data source paths
|
||||
const TEST_PATH = 'C:/Shares/test';
|
||||
const RECOVERY_PATH = 'C:/Shares/Recovery-TEST';
|
||||
const HISTLOGS_PATH = path.join(TEST_PATH, 'Ate/HISTLOGS');
|
||||
|
||||
// Log types and their parsers
|
||||
const LOG_TYPES = {
|
||||
'DSCLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'5BLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'8BLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'PWRLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'SCTLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'VASLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'7BLOG': { parser: 'csvline', ext: '.DAT' }
|
||||
};
|
||||
|
||||
// Find all files of a specific type in a directory
|
||||
function findFiles(dir, pattern, recursive = true) {
|
||||
const results = [];
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(dir)) return results;
|
||||
|
||||
const items = fs.readdirSync(dir, { withFileTypes: true });
|
||||
|
||||
for (const item of items) {
|
||||
const fullPath = path.join(dir, item.name);
|
||||
|
||||
if (item.isDirectory() && recursive) {
|
||||
results.push(...findFiles(fullPath, pattern, recursive));
|
||||
} else if (item.isFile()) {
|
||||
if (pattern.test(item.name)) {
|
||||
results.push(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore permission errors
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Parse records from a file (sync -- file I/O only)
|
||||
function parseFile(filePath, logType, parser) {
|
||||
const testStation = extractTestStation(filePath);
|
||||
|
||||
switch (parser) {
|
||||
case 'multiline':
|
||||
return parseMultilineFile(filePath, logType, testStation);
|
||||
case 'csvline':
|
||||
return parseCsvFile(filePath, testStation);
|
||||
case 'shtfile':
|
||||
return parseShtFile(filePath, testStation);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Batch insert records into PostgreSQL
|
||||
async function insertBatch(txClient, records) {
|
||||
let imported = 0;
|
||||
for (const record of records) {
|
||||
try {
|
||||
const result = await txClient.execute(
|
||||
`INSERT INTO test_records
|
||||
(log_type, model_number, serial_number, test_date, test_station, overall_result, raw_data, source_file)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
ON CONFLICT (log_type, model_number, serial_number, test_date, test_station)
|
||||
DO UPDATE SET raw_data = EXCLUDED.raw_data, overall_result = EXCLUDED.overall_result`,
|
||||
[
|
||||
record.log_type,
|
||||
record.model_number,
|
||||
record.serial_number,
|
||||
record.test_date,
|
||||
record.test_station,
|
||||
record.overall_result,
|
||||
record.raw_data,
|
||||
record.source_file
|
||||
]
|
||||
);
|
||||
if (result.rowCount > 0) imported++;
|
||||
} catch (err) {
|
||||
// Constraint error - skip
|
||||
}
|
||||
}
|
||||
return imported;
|
||||
}
|
||||
|
||||
// Import records from a file
|
||||
async function importFile(txClient, filePath, logType, parser) {
|
||||
let records = [];
|
||||
|
||||
try {
|
||||
records = parseFile(filePath, logType, parser);
|
||||
const imported = await insertBatch(txClient, records);
|
||||
return { total: records.length, imported };
|
||||
} catch (err) {
|
||||
console.error(`Error importing ${filePath}: ${err.message}`);
|
||||
return { total: 0, imported: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// Import from HISTLOGS (master consolidated logs)
|
||||
async function importHistlogs(txClient) {
|
||||
console.log('\n=== Importing from HISTLOGS ===');
|
||||
|
||||
let totalImported = 0;
|
||||
let totalRecords = 0;
|
||||
|
||||
for (const [logType, config] of Object.entries(LOG_TYPES)) {
|
||||
const logDir = path.join(HISTLOGS_PATH, logType);
|
||||
|
||||
if (!fs.existsSync(logDir)) {
|
||||
console.log(` ${logType}: directory not found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const files = findFiles(logDir, new RegExp(`\\${config.ext}$`, 'i'), false);
|
||||
console.log(` ${logType}: found ${files.length} files`);
|
||||
|
||||
for (const file of files) {
|
||||
const { total, imported } = await importFile(txClient, file, logType, config.parser);
|
||||
totalRecords += total;
|
||||
totalImported += imported;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` HISTLOGS total: ${totalImported} records imported (${totalRecords} parsed)`);
|
||||
return totalImported;
|
||||
}
|
||||
|
||||
// Import from test station logs
|
||||
async function importStationLogs(txClient, basePath, label) {
|
||||
console.log(`\n=== Importing from ${label} ===`);
|
||||
|
||||
let totalImported = 0;
|
||||
let totalRecords = 0;
|
||||
|
||||
const stationPattern = /^TS-\d+[LR]?$/i;
|
||||
let stations = [];
|
||||
|
||||
try {
|
||||
const items = fs.readdirSync(basePath, { withFileTypes: true });
|
||||
stations = items
|
||||
.filter(i => i.isDirectory() && stationPattern.test(i.name))
|
||||
.map(i => i.name);
|
||||
} catch (err) {
|
||||
console.log(` Error reading ${basePath}: ${err.message}`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
console.log(` Found stations: ${stations.join(', ')}`);
|
||||
|
||||
for (const station of stations) {
|
||||
const logsDir = path.join(basePath, station, 'LOGS');
|
||||
|
||||
if (!fs.existsSync(logsDir)) continue;
|
||||
|
||||
for (const [logType, config] of Object.entries(LOG_TYPES)) {
|
||||
const logDir = path.join(logsDir, logType);
|
||||
|
||||
if (!fs.existsSync(logDir)) continue;
|
||||
|
||||
const files = findFiles(logDir, new RegExp(`\\${config.ext}$`, 'i'), false);
|
||||
|
||||
for (const file of files) {
|
||||
const { total, imported } = await importFile(txClient, file, logType, config.parser);
|
||||
totalRecords += total;
|
||||
totalImported += imported;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also import SHT files
|
||||
const shtFiles = findFiles(basePath, /\.SHT$/i, true);
|
||||
console.log(` Found ${shtFiles.length} SHT files`);
|
||||
|
||||
for (const file of shtFiles) {
|
||||
const { total, imported } = await importFile(txClient, file, 'SHT', 'shtfile');
|
||||
totalRecords += total;
|
||||
totalImported += imported;
|
||||
}
|
||||
|
||||
console.log(` ${label} total: ${totalImported} records imported (${totalRecords} parsed)`);
|
||||
return totalImported;
|
||||
}
|
||||
|
||||
// Import from Recovery-TEST backups (newest first)
|
||||
async function importRecoveryBackups(txClient) {
|
||||
console.log('\n=== Importing from Recovery-TEST backups ===');
|
||||
|
||||
if (!fs.existsSync(RECOVERY_PATH)) {
|
||||
console.log(' Recovery-TEST directory not found');
|
||||
return 0;
|
||||
}
|
||||
|
||||
const backups = fs.readdirSync(RECOVERY_PATH, { withFileTypes: true })
|
||||
.filter(i => i.isDirectory() && /^\d{2}-\d{2}-\d{2}$/.test(i.name))
|
||||
.map(i => i.name)
|
||||
.sort()
|
||||
.reverse();
|
||||
|
||||
console.log(` Found backup dates: ${backups.join(', ')}`);
|
||||
|
||||
let totalImported = 0;
|
||||
|
||||
for (const backup of backups) {
|
||||
const backupPath = path.join(RECOVERY_PATH, backup);
|
||||
const imported = await importStationLogs(txClient, backupPath, `Recovery-TEST/${backup}`);
|
||||
totalImported += imported;
|
||||
}
|
||||
|
||||
return totalImported;
|
||||
}
|
||||
|
||||
// Main import function
|
||||
async function runImport() {
|
||||
console.log('========================================');
|
||||
console.log('Test Data Import');
|
||||
console.log('========================================');
|
||||
console.log(`Start time: ${new Date().toISOString()}`);
|
||||
|
||||
let grandTotal = 0;
|
||||
|
||||
await db.transaction(async (txClient) => {
|
||||
grandTotal += await importHistlogs(txClient);
|
||||
grandTotal += await importRecoveryBackups(txClient);
|
||||
grandTotal += await importStationLogs(txClient, TEST_PATH, 'test');
|
||||
});
|
||||
|
||||
const stats = await db.queryOne('SELECT COUNT(*) as count FROM test_records');
|
||||
|
||||
console.log('\n========================================');
|
||||
console.log('Import Complete');
|
||||
console.log('========================================');
|
||||
console.log(`Total records in database: ${stats.count}`);
|
||||
console.log(`End time: ${new Date().toISOString()}`);
|
||||
|
||||
await db.close();
|
||||
}
|
||||
|
||||
// Import a single file (for incremental imports from sync)
|
||||
async function importSingleFile(filePath) {
|
||||
console.log(`Importing: ${filePath}`);
|
||||
|
||||
let logType = null;
|
||||
let parser = null;
|
||||
|
||||
for (const [type, config] of Object.entries(LOG_TYPES)) {
|
||||
if (filePath.includes(type)) {
|
||||
logType = type;
|
||||
parser = config.parser;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!logType) {
|
||||
if (/\.SHT$/i.test(filePath)) {
|
||||
logType = 'SHT';
|
||||
parser = 'shtfile';
|
||||
} else {
|
||||
console.log(` Unknown log type for: ${filePath}`);
|
||||
return { total: 0, imported: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
let result;
|
||||
await db.transaction(async (txClient) => {
|
||||
result = await importFile(txClient, filePath, logType, parser);
|
||||
});
|
||||
|
||||
console.log(` Imported ${result.imported} of ${result.total} records`);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Import multiple files (for batch incremental imports)
|
||||
async function importFiles(filePaths) {
|
||||
console.log(`\n========================================`);
|
||||
console.log(`Incremental Import: ${filePaths.length} files`);
|
||||
console.log(`========================================`);
|
||||
|
||||
let totalImported = 0;
|
||||
let totalRecords = 0;
|
||||
|
||||
await db.transaction(async (txClient) => {
|
||||
for (const filePath of filePaths) {
|
||||
let logType = null;
|
||||
let parser = null;
|
||||
|
||||
for (const [type, config] of Object.entries(LOG_TYPES)) {
|
||||
if (filePath.includes(type)) {
|
||||
logType = type;
|
||||
parser = config.parser;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!logType) {
|
||||
if (/\.SHT$/i.test(filePath)) {
|
||||
logType = 'SHT';
|
||||
parser = 'shtfile';
|
||||
} else {
|
||||
console.log(` Skipping unknown type: ${filePath}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const { total, imported } = await importFile(txClient, filePath, logType, parser);
|
||||
totalRecords += total;
|
||||
totalImported += imported;
|
||||
console.log(` ${path.basename(filePath)}: ${imported}/${total} records`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`\nTotal: ${totalImported} records imported (${totalRecords} parsed)`);
|
||||
|
||||
// Export datasheets for newly imported records
|
||||
if (totalImported > 0) {
|
||||
try {
|
||||
const { loadAllSpecs } = require('../parsers/spec-reader');
|
||||
const { exportNewRecords } = require('./export-datasheets');
|
||||
const specMap = loadAllSpecs();
|
||||
await exportNewRecords(specMap, filePaths);
|
||||
} catch (err) {
|
||||
console.error(`[EXPORT] Datasheet export failed: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
return { total: totalRecords, imported: totalImported };
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
if (require.main === module) {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length > 0 && args[0] === '--file') {
|
||||
const files = args.slice(1);
|
||||
if (files.length === 0) {
|
||||
console.log('Usage: node import.js --file <file1> [file2] ...');
|
||||
process.exit(1);
|
||||
}
|
||||
importFiles(files).then(() => db.close()).catch(console.error);
|
||||
} else if (args.length > 0 && args[0] === '--help') {
|
||||
console.log('Usage:');
|
||||
console.log(' node import.js Full import from all sources');
|
||||
console.log(' node import.js --file <f> Import specific file(s)');
|
||||
process.exit(0);
|
||||
} else {
|
||||
runImport().catch(console.error);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { runImport, importSingleFile, importFiles };
|
||||
@@ -0,0 +1,397 @@
|
||||
/**
|
||||
* Data Import Script
|
||||
* Imports test data from DAT and SHT files into SQLite database
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const Database = require('better-sqlite3');
|
||||
|
||||
const { parseMultilineFile, extractTestStation } = require('../parsers/multiline');
|
||||
const { parseCsvFile } = require('../parsers/csvline');
|
||||
const { parseShtFile } = require('../parsers/shtfile');
|
||||
|
||||
// Configuration
|
||||
const DB_PATH = path.join(__dirname, 'testdata.db');
|
||||
const SCHEMA_PATH = path.join(__dirname, 'schema.sql');
|
||||
|
||||
// Data source paths
|
||||
const TEST_PATH = 'C:/Shares/test';
|
||||
const RECOVERY_PATH = 'C:/Shares/Recovery-TEST';
|
||||
const HISTLOGS_PATH = path.join(TEST_PATH, 'Ate/HISTLOGS');
|
||||
|
||||
// Log types and their parsers
|
||||
const LOG_TYPES = {
|
||||
'DSCLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'5BLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'8BLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'PWRLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'SCTLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'VASLOG': { parser: 'multiline', ext: '.DAT' },
|
||||
'7BLOG': { parser: 'csvline', ext: '.DAT' }
|
||||
};
|
||||
|
||||
// Initialize database
|
||||
function initDatabase() {
|
||||
console.log('Initializing database...');
|
||||
const db = new Database(DB_PATH);
|
||||
|
||||
// Read and execute schema
|
||||
const schema = fs.readFileSync(SCHEMA_PATH, 'utf8');
|
||||
db.exec(schema);
|
||||
|
||||
console.log('Database initialized.');
|
||||
return db;
|
||||
}
|
||||
|
||||
// Prepare insert statement
|
||||
// Uses INSERT OR REPLACE so re-tested devices keep the latest result
|
||||
// UNIQUE constraint: (log_type, model_number, serial_number, test_date, test_station)
|
||||
function prepareInsert(db) {
|
||||
return db.prepare(`
|
||||
INSERT OR REPLACE INTO test_records
|
||||
(log_type, model_number, serial_number, test_date, test_station, overall_result, raw_data, source_file)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
}
|
||||
|
||||
// Find all files of a specific type in a directory
|
||||
function findFiles(dir, pattern, recursive = true) {
|
||||
const results = [];
|
||||
|
||||
try {
|
||||
if (!fs.existsSync(dir)) return results;
|
||||
|
||||
const items = fs.readdirSync(dir, { withFileTypes: true });
|
||||
|
||||
for (const item of items) {
|
||||
const fullPath = path.join(dir, item.name);
|
||||
|
||||
if (item.isDirectory() && recursive) {
|
||||
results.push(...findFiles(fullPath, pattern, recursive));
|
||||
} else if (item.isFile()) {
|
||||
if (pattern.test(item.name)) {
|
||||
results.push(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore permission errors
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Import records from a file
|
||||
function importFile(db, insertStmt, filePath, logType, parser) {
|
||||
let records = [];
|
||||
const testStation = extractTestStation(filePath);
|
||||
|
||||
try {
|
||||
switch (parser) {
|
||||
case 'multiline':
|
||||
records = parseMultilineFile(filePath, logType, testStation);
|
||||
break;
|
||||
case 'csvline':
|
||||
records = parseCsvFile(filePath, testStation);
|
||||
break;
|
||||
case 'shtfile':
|
||||
records = parseShtFile(filePath, testStation);
|
||||
break;
|
||||
}
|
||||
|
||||
let imported = 0;
|
||||
for (const record of records) {
|
||||
try {
|
||||
const result = insertStmt.run(
|
||||
record.log_type,
|
||||
record.model_number,
|
||||
record.serial_number,
|
||||
record.test_date,
|
||||
record.test_station,
|
||||
record.overall_result,
|
||||
record.raw_data,
|
||||
record.source_file
|
||||
);
|
||||
if (result.changes > 0) imported++;
|
||||
} catch (err) {
|
||||
// Duplicate or constraint error - skip
|
||||
}
|
||||
}
|
||||
|
||||
return { total: records.length, imported };
|
||||
} catch (err) {
|
||||
console.error(`Error importing ${filePath}: ${err.message}`);
|
||||
return { total: 0, imported: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// Import from HISTLOGS (master consolidated logs)
|
||||
function importHistlogs(db, insertStmt) {
|
||||
console.log('\n=== Importing from HISTLOGS ===');
|
||||
|
||||
let totalImported = 0;
|
||||
let totalRecords = 0;
|
||||
|
||||
for (const [logType, config] of Object.entries(LOG_TYPES)) {
|
||||
const logDir = path.join(HISTLOGS_PATH, logType);
|
||||
|
||||
if (!fs.existsSync(logDir)) {
|
||||
console.log(` ${logType}: directory not found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const files = findFiles(logDir, new RegExp(`\\${config.ext}$`, 'i'), false);
|
||||
console.log(` ${logType}: found ${files.length} files`);
|
||||
|
||||
for (const file of files) {
|
||||
const { total, imported } = importFile(db, insertStmt, file, logType, config.parser);
|
||||
totalRecords += total;
|
||||
totalImported += imported;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` HISTLOGS total: ${totalImported} records imported (${totalRecords} parsed)`);
|
||||
return totalImported;
|
||||
}
|
||||
|
||||
// Import from test station logs
|
||||
function importStationLogs(db, insertStmt, basePath, label) {
|
||||
console.log(`\n=== Importing from ${label} ===`);
|
||||
|
||||
let totalImported = 0;
|
||||
let totalRecords = 0;
|
||||
|
||||
// Find all test station directories (TS-1, TS-27, TS-8L, TS-10R, etc.)
|
||||
const stationPattern = /^TS-\d+[LR]?$/i;
|
||||
let stations = [];
|
||||
|
||||
try {
|
||||
const items = fs.readdirSync(basePath, { withFileTypes: true });
|
||||
stations = items
|
||||
.filter(i => i.isDirectory() && stationPattern.test(i.name))
|
||||
.map(i => i.name);
|
||||
} catch (err) {
|
||||
console.log(` Error reading ${basePath}: ${err.message}`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
console.log(` Found stations: ${stations.join(', ')}`);
|
||||
|
||||
for (const station of stations) {
|
||||
const logsDir = path.join(basePath, station, 'LOGS');
|
||||
|
||||
if (!fs.existsSync(logsDir)) continue;
|
||||
|
||||
for (const [logType, config] of Object.entries(LOG_TYPES)) {
|
||||
const logDir = path.join(logsDir, logType);
|
||||
|
||||
if (!fs.existsSync(logDir)) continue;
|
||||
|
||||
const files = findFiles(logDir, new RegExp(`\\${config.ext}$`, 'i'), false);
|
||||
|
||||
for (const file of files) {
|
||||
const { total, imported } = importFile(db, insertStmt, file, logType, config.parser);
|
||||
totalRecords += total;
|
||||
totalImported += imported;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also import SHT files
|
||||
const shtFiles = findFiles(basePath, /\.SHT$/i, true);
|
||||
console.log(` Found ${shtFiles.length} SHT files`);
|
||||
|
||||
for (const file of shtFiles) {
|
||||
const { total, imported } = importFile(db, insertStmt, file, 'SHT', 'shtfile');
|
||||
totalRecords += total;
|
||||
totalImported += imported;
|
||||
}
|
||||
|
||||
console.log(` ${label} total: ${totalImported} records imported (${totalRecords} parsed)`);
|
||||
return totalImported;
|
||||
}
|
||||
|
||||
// Import from Recovery-TEST backups (newest first)
|
||||
function importRecoveryBackups(db, insertStmt) {
|
||||
console.log('\n=== Importing from Recovery-TEST backups ===');
|
||||
|
||||
if (!fs.existsSync(RECOVERY_PATH)) {
|
||||
console.log(' Recovery-TEST directory not found');
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get backup dates, sort newest first
|
||||
const backups = fs.readdirSync(RECOVERY_PATH, { withFileTypes: true })
|
||||
.filter(i => i.isDirectory() && /^\d{2}-\d{2}-\d{2}$/.test(i.name))
|
||||
.map(i => i.name)
|
||||
.sort()
|
||||
.reverse();
|
||||
|
||||
console.log(` Found backup dates: ${backups.join(', ')}`);
|
||||
|
||||
let totalImported = 0;
|
||||
|
||||
for (const backup of backups) {
|
||||
const backupPath = path.join(RECOVERY_PATH, backup);
|
||||
const imported = importStationLogs(db, insertStmt, backupPath, `Recovery-TEST/${backup}`);
|
||||
totalImported += imported;
|
||||
}
|
||||
|
||||
return totalImported;
|
||||
}
|
||||
|
||||
// Main import function
|
||||
async function runImport() {
|
||||
console.log('========================================');
|
||||
console.log('Test Data Import');
|
||||
console.log('========================================');
|
||||
console.log(`Database: ${DB_PATH}`);
|
||||
console.log(`Start time: ${new Date().toISOString()}`);
|
||||
|
||||
const db = initDatabase();
|
||||
const insertStmt = prepareInsert(db);
|
||||
|
||||
let grandTotal = 0;
|
||||
|
||||
// Use transaction for performance
|
||||
const importAll = db.transaction(() => {
|
||||
// 1. Import HISTLOGS first (authoritative)
|
||||
grandTotal += importHistlogs(db, insertStmt);
|
||||
|
||||
// 2. Import Recovery backups (newest first)
|
||||
grandTotal += importRecoveryBackups(db, insertStmt);
|
||||
|
||||
// 3. Import current test folder
|
||||
grandTotal += importStationLogs(db, insertStmt, TEST_PATH, 'test');
|
||||
});
|
||||
|
||||
importAll();
|
||||
|
||||
// Get final stats
|
||||
const stats = db.prepare('SELECT COUNT(*) as count FROM test_records').get();
|
||||
|
||||
console.log('\n========================================');
|
||||
console.log('Import Complete');
|
||||
console.log('========================================');
|
||||
console.log(`Total records in database: ${stats.count}`);
|
||||
console.log(`End time: ${new Date().toISOString()}`);
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
// Import a single file (for incremental imports from sync)
|
||||
function importSingleFile(filePath) {
|
||||
console.log(`Importing: ${filePath}`);
|
||||
|
||||
const db = new Database(DB_PATH);
|
||||
const insertStmt = prepareInsert(db);
|
||||
|
||||
// Determine log type from path
|
||||
let logType = null;
|
||||
let parser = null;
|
||||
|
||||
for (const [type, config] of Object.entries(LOG_TYPES)) {
|
||||
if (filePath.includes(type)) {
|
||||
logType = type;
|
||||
parser = config.parser;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!logType) {
|
||||
// Check for SHT files
|
||||
if (/\.SHT$/i.test(filePath)) {
|
||||
logType = 'SHT';
|
||||
parser = 'shtfile';
|
||||
} else {
|
||||
console.log(` Unknown log type for: ${filePath}`);
|
||||
db.close();
|
||||
return { total: 0, imported: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
const result = importFile(db, insertStmt, filePath, logType, parser);
|
||||
|
||||
console.log(` Imported ${result.imported} of ${result.total} records`);
|
||||
db.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Import multiple files (for batch incremental imports)
|
||||
function importFiles(filePaths) {
|
||||
console.log(`\n========================================`);
|
||||
console.log(`Incremental Import: ${filePaths.length} files`);
|
||||
console.log(`========================================`);
|
||||
|
||||
const db = new Database(DB_PATH);
|
||||
const insertStmt = prepareInsert(db);
|
||||
|
||||
let totalImported = 0;
|
||||
let totalRecords = 0;
|
||||
|
||||
const importBatch = db.transaction(() => {
|
||||
for (const filePath of filePaths) {
|
||||
// Determine log type from path
|
||||
let logType = null;
|
||||
let parser = null;
|
||||
|
||||
for (const [type, config] of Object.entries(LOG_TYPES)) {
|
||||
if (filePath.includes(type)) {
|
||||
logType = type;
|
||||
parser = config.parser;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!logType) {
|
||||
if (/\.SHT$/i.test(filePath)) {
|
||||
logType = 'SHT';
|
||||
parser = 'shtfile';
|
||||
} else {
|
||||
console.log(` Skipping unknown type: ${filePath}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const { total, imported } = importFile(db, insertStmt, filePath, logType, parser);
|
||||
totalRecords += total;
|
||||
totalImported += imported;
|
||||
console.log(` ${path.basename(filePath)}: ${imported}/${total} records`);
|
||||
}
|
||||
});
|
||||
|
||||
importBatch();
|
||||
|
||||
console.log(`\nTotal: ${totalImported} records imported (${totalRecords} parsed)`);
|
||||
db.close();
|
||||
|
||||
return { total: totalRecords, imported: totalImported };
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
if (require.main === module) {
|
||||
// Check for command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length > 0 && args[0] === '--file') {
|
||||
// Import specific file(s)
|
||||
const files = args.slice(1);
|
||||
if (files.length === 0) {
|
||||
console.log('Usage: node import.js --file <file1> [file2] ...');
|
||||
process.exit(1);
|
||||
}
|
||||
importFiles(files);
|
||||
} else if (args.length > 0 && args[0] === '--help') {
|
||||
console.log('Usage:');
|
||||
console.log(' node import.js Full import from all sources');
|
||||
console.log(' node import.js --file <f> Import specific file(s)');
|
||||
process.exit(0);
|
||||
} else {
|
||||
// Full import
|
||||
runImport().catch(console.error);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { runImport, importSingleFile, importFiles };
|
||||
@@ -0,0 +1,260 @@
|
||||
/**
|
||||
* SQLite to PostgreSQL Data Migration
|
||||
*
|
||||
* Streams all data from the SQLite testdata.db into PostgreSQL.
|
||||
* Uses batch INSERTs for performance.
|
||||
*
|
||||
* Usage:
|
||||
* node migrate-data.js Migrate all tables
|
||||
* node migrate-data.js --skip-tsvector Skip tsvector rebuild (faster, trigger handles it)
|
||||
* node migrate-data.js --table test_records Migrate only one table
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const Database = require('better-sqlite3');
|
||||
const db = require('./db');
|
||||
|
||||
const SQLITE_PATH = path.join(__dirname, 'testdata.db');
|
||||
const BATCH_SIZE = 5000;
|
||||
|
||||
async function migrateTestRecords(sqlite) {
|
||||
console.log('\n--- Migrating test_records ---');
|
||||
|
||||
const total = sqlite.prepare('SELECT COUNT(*) as cnt FROM test_records').get().cnt;
|
||||
console.log(` Source records: ${total.toLocaleString()}`);
|
||||
|
||||
// Disable triggers during bulk load for performance
|
||||
await db.execute('ALTER TABLE test_records DISABLE TRIGGER trg_search_vector');
|
||||
|
||||
const stmt = sqlite.prepare('SELECT * FROM test_records ORDER BY id');
|
||||
let migrated = 0;
|
||||
let batch = [];
|
||||
|
||||
for (const row of stmt.iterate()) {
|
||||
batch.push(row);
|
||||
|
||||
if (batch.length >= BATCH_SIZE) {
|
||||
await insertTestRecordsBatch(batch);
|
||||
migrated += batch.length;
|
||||
batch = [];
|
||||
process.stdout.write(`\r Migrated: ${migrated.toLocaleString()} / ${total.toLocaleString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Flush remaining
|
||||
if (batch.length > 0) {
|
||||
await insertTestRecordsBatch(batch);
|
||||
migrated += batch.length;
|
||||
}
|
||||
|
||||
console.log(`\r Migrated: ${migrated.toLocaleString()} / ${total.toLocaleString()}`);
|
||||
|
||||
// Rebuild search_vector for all rows
|
||||
console.log(' Rebuilding search_vector (this may take a few minutes)...');
|
||||
await db.execute(`
|
||||
UPDATE test_records SET search_vector = to_tsvector('english',
|
||||
COALESCE(serial_number, '') || ' ' ||
|
||||
COALESCE(model_number, '') || ' ' ||
|
||||
COALESCE(raw_data, '')
|
||||
)
|
||||
`);
|
||||
console.log(' search_vector rebuilt.');
|
||||
|
||||
// Re-enable trigger
|
||||
await db.execute('ALTER TABLE test_records ENABLE TRIGGER trg_search_vector');
|
||||
|
||||
// Reset sequence to max id
|
||||
await db.execute(`SELECT setval('test_records_id_seq', (SELECT COALESCE(MAX(id), 1) FROM test_records))`);
|
||||
|
||||
return migrated;
|
||||
}
|
||||
|
||||
async function insertTestRecordsBatch(batch) {
|
||||
// Build a multi-row INSERT
|
||||
const cols = ['id', 'log_type', 'model_number', 'serial_number', 'test_date',
|
||||
'test_station', 'overall_result', 'raw_data', 'source_file',
|
||||
'import_date', 'datasheet_exported_at', 'forweb_exported_at', 'work_order'];
|
||||
|
||||
const values = [];
|
||||
const params = [];
|
||||
let paramIdx = 0;
|
||||
|
||||
for (const row of batch) {
|
||||
const placeholders = cols.map(() => {
|
||||
paramIdx++;
|
||||
return `$${paramIdx}`;
|
||||
});
|
||||
values.push(`(${placeholders.join(',')})`);
|
||||
|
||||
params.push(
|
||||
row.id,
|
||||
row.log_type,
|
||||
row.model_number,
|
||||
row.serial_number,
|
||||
row.test_date,
|
||||
row.test_station,
|
||||
row.overall_result,
|
||||
row.raw_data,
|
||||
row.source_file,
|
||||
row.import_date,
|
||||
row.datasheet_exported_at,
|
||||
row.forweb_exported_at,
|
||||
row.work_order
|
||||
);
|
||||
}
|
||||
|
||||
const sql = `INSERT INTO test_records (${cols.join(',')})
|
||||
VALUES ${values.join(',')}
|
||||
ON CONFLICT (log_type, model_number, serial_number, test_date, test_station)
|
||||
DO NOTHING`;
|
||||
|
||||
await db.execute(sql, params);
|
||||
}
|
||||
|
||||
async function migrateWorkOrders(sqlite) {
|
||||
console.log('\n--- Migrating work_orders ---');
|
||||
|
||||
const rows = sqlite.prepare('SELECT * FROM work_orders ORDER BY id').all();
|
||||
console.log(` Source records: ${rows.length.toLocaleString()}`);
|
||||
|
||||
let migrated = 0;
|
||||
|
||||
const cols = ['wo_number', 'wo_date', 'program', 'version',
|
||||
'lib_version', 'test_station', 'source_file', 'import_date'];
|
||||
|
||||
for (let i = 0; i < rows.length; i += BATCH_SIZE) {
|
||||
const batch = rows.slice(i, i + BATCH_SIZE);
|
||||
const values = [];
|
||||
const params = [];
|
||||
let paramIdx = 0;
|
||||
|
||||
for (const row of batch) {
|
||||
const placeholders = cols.map(() => { paramIdx++; return `$${paramIdx}`; });
|
||||
values.push(`(${placeholders.join(',')})`);
|
||||
params.push(row.wo_number, row.wo_date, row.program, row.version,
|
||||
row.lib_version, row.test_station, row.source_file, row.import_date);
|
||||
}
|
||||
|
||||
await db.execute(
|
||||
`INSERT INTO work_orders (${cols.join(',')}) VALUES ${values.join(',')}
|
||||
ON CONFLICT (wo_number, test_station) DO NOTHING`,
|
||||
params
|
||||
);
|
||||
migrated += batch.length;
|
||||
}
|
||||
|
||||
console.log(` Migrated: ${migrated.toLocaleString()}`);
|
||||
return migrated;
|
||||
}
|
||||
|
||||
async function migrateWorkOrderLines(sqlite) {
|
||||
console.log('\n--- Migrating work_order_lines ---');
|
||||
|
||||
const total = sqlite.prepare('SELECT COUNT(*) as cnt FROM work_order_lines').get().cnt;
|
||||
console.log(` Source records: ${total.toLocaleString()}`);
|
||||
|
||||
const stmt = sqlite.prepare('SELECT * FROM work_order_lines ORDER BY id');
|
||||
let migrated = 0;
|
||||
let batch = [];
|
||||
|
||||
for (const row of stmt.iterate()) {
|
||||
batch.push(row);
|
||||
|
||||
if (batch.length >= BATCH_SIZE) {
|
||||
await insertWoLinesBatch(batch);
|
||||
migrated += batch.length;
|
||||
batch = [];
|
||||
process.stdout.write(`\r Migrated: ${migrated.toLocaleString()} / ${total.toLocaleString()}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (batch.length > 0) {
|
||||
await insertWoLinesBatch(batch);
|
||||
migrated += batch.length;
|
||||
}
|
||||
|
||||
console.log(`\r Migrated: ${migrated.toLocaleString()} / ${total.toLocaleString()}`);
|
||||
return migrated;
|
||||
}
|
||||
|
||||
async function insertWoLinesBatch(batch) {
|
||||
const cols = ['wo_number', 'serial_number', 'status', 'model_number',
|
||||
'ds_filename', 'test_date', 'test_time', 'test_station'];
|
||||
const values = [];
|
||||
const params = [];
|
||||
let paramIdx = 0;
|
||||
|
||||
for (const row of batch) {
|
||||
const placeholders = cols.map(() => { paramIdx++; return `$${paramIdx}`; });
|
||||
values.push(`(${placeholders.join(',')})`);
|
||||
params.push(row.wo_number, row.serial_number, row.status,
|
||||
row.model_number, row.ds_filename, row.test_date, row.test_time, row.test_station);
|
||||
}
|
||||
|
||||
await db.execute(
|
||||
`INSERT INTO work_order_lines (${cols.join(',')}) VALUES ${values.join(',')}
|
||||
ON CONFLICT (wo_number, serial_number, test_date, test_time) DO NOTHING`,
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const tableArg = args.indexOf('--table');
|
||||
const targetTable = tableArg >= 0 ? args[tableArg + 1] : null;
|
||||
|
||||
console.log('========================================');
|
||||
console.log('SQLite -> PostgreSQL Data Migration');
|
||||
console.log('========================================');
|
||||
console.log(`SQLite: ${SQLITE_PATH}`);
|
||||
console.log(`Start: ${new Date().toISOString()}`);
|
||||
|
||||
const sqlite = new Database(SQLITE_PATH, { readonly: true });
|
||||
|
||||
try {
|
||||
if (!targetTable || targetTable === 'test_records') {
|
||||
await migrateTestRecords(sqlite);
|
||||
}
|
||||
if (!targetTable || targetTable === 'work_orders') {
|
||||
await migrateWorkOrders(sqlite);
|
||||
}
|
||||
if (!targetTable || targetTable === 'work_order_lines') {
|
||||
await migrateWorkOrderLines(sqlite);
|
||||
}
|
||||
|
||||
// VACUUM ANALYZE
|
||||
console.log('\n--- Running VACUUM ANALYZE ---');
|
||||
await db.execute('VACUUM ANALYZE test_records');
|
||||
await db.execute('VACUUM ANALYZE work_orders');
|
||||
await db.execute('VACUUM ANALYZE work_order_lines');
|
||||
console.log(' Done.');
|
||||
|
||||
// Verify counts
|
||||
console.log('\n--- Verification ---');
|
||||
const pgTestCount = await db.queryOne('SELECT COUNT(*) as cnt FROM test_records');
|
||||
const pgWoCount = await db.queryOne('SELECT COUNT(*) as cnt FROM work_orders');
|
||||
const pgWolCount = await db.queryOne('SELECT COUNT(*) as cnt FROM work_order_lines');
|
||||
|
||||
const sqliteTestCount = sqlite.prepare('SELECT COUNT(*) as cnt FROM test_records').get().cnt;
|
||||
const sqliteWoCount = sqlite.prepare('SELECT COUNT(*) as cnt FROM work_orders').get().cnt;
|
||||
const sqliteWolCount = sqlite.prepare('SELECT COUNT(*) as cnt FROM work_order_lines').get().cnt;
|
||||
|
||||
console.log(` test_records: SQLite=${sqliteTestCount.toLocaleString()} PG=${parseInt(pgTestCount.cnt).toLocaleString()} ${parseInt(pgTestCount.cnt) === sqliteTestCount ? '[OK]' : '[MISMATCH]'}`);
|
||||
console.log(` work_orders: SQLite=${sqliteWoCount.toLocaleString()} PG=${parseInt(pgWoCount.cnt).toLocaleString()} ${parseInt(pgWoCount.cnt) === sqliteWoCount ? '[OK]' : '[MISMATCH]'}`);
|
||||
console.log(` work_order_lines: SQLite=${sqliteWolCount.toLocaleString()} PG=${parseInt(pgWolCount.cnt).toLocaleString()} ${parseInt(pgWolCount.cnt) === sqliteWolCount ? '[OK]' : '[MISMATCH]'}`);
|
||||
|
||||
} finally {
|
||||
sqlite.close();
|
||||
await db.close();
|
||||
}
|
||||
|
||||
console.log(`\n========================================`);
|
||||
console.log(`Migration Complete`);
|
||||
console.log(`========================================`);
|
||||
console.log(`End: ${new Date().toISOString()}`);
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('Migration failed:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
-- TestDataDB PostgreSQL Schema
|
||||
-- Migrated from SQLite schema.sql
|
||||
-- PostgreSQL 18 on AD2 (192.168.0.6)
|
||||
|
||||
-- Main test records table
|
||||
CREATE TABLE IF NOT EXISTS test_records (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
log_type TEXT NOT NULL,
|
||||
model_number TEXT NOT NULL,
|
||||
serial_number TEXT NOT NULL,
|
||||
test_date TEXT NOT NULL,
|
||||
test_station TEXT,
|
||||
overall_result TEXT,
|
||||
raw_data TEXT,
|
||||
source_file TEXT,
|
||||
import_date TIMESTAMPTZ DEFAULT NOW(),
|
||||
datasheet_exported_at TIMESTAMPTZ DEFAULT NULL,
|
||||
forweb_exported_at TIMESTAMPTZ DEFAULT NULL,
|
||||
work_order TEXT DEFAULT NULL,
|
||||
search_vector tsvector,
|
||||
UNIQUE(log_type, model_number, serial_number, test_date, test_station)
|
||||
);
|
||||
|
||||
-- Indexes for fast searching
|
||||
CREATE INDEX IF NOT EXISTS idx_serial ON test_records(serial_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_model ON test_records(model_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_date ON test_records(test_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_model_serial ON test_records(model_number, serial_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_result ON test_records(overall_result);
|
||||
CREATE INDEX IF NOT EXISTS idx_log_type ON test_records(log_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_test_wo ON test_records(work_order);
|
||||
|
||||
-- Partial index for unexported PASS records (speeds up export queries)
|
||||
CREATE INDEX IF NOT EXISTS idx_unexported_pass ON test_records(overall_result, forweb_exported_at)
|
||||
WHERE overall_result = 'PASS' AND forweb_exported_at IS NULL;
|
||||
|
||||
-- GIN index for full-text search (replaces SQLite FTS5 virtual table)
|
||||
CREATE INDEX IF NOT EXISTS idx_search_vector ON test_records USING GIN(search_vector);
|
||||
|
||||
-- Trigger function to maintain search_vector on INSERT/UPDATE
|
||||
CREATE OR REPLACE FUNCTION update_search_vector() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
NEW.search_vector := to_tsvector('english',
|
||||
COALESCE(NEW.serial_number, '') || ' ' ||
|
||||
COALESCE(NEW.model_number, '') || ' ' ||
|
||||
COALESCE(NEW.raw_data, '')
|
||||
);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Drop trigger if exists, then create
|
||||
DROP TRIGGER IF EXISTS trg_search_vector ON test_records;
|
||||
CREATE TRIGGER trg_search_vector
|
||||
BEFORE INSERT OR UPDATE ON test_records
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_search_vector();
|
||||
|
||||
-- Work orders table
|
||||
CREATE TABLE IF NOT EXISTS work_orders (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
wo_number TEXT NOT NULL,
|
||||
wo_date TEXT,
|
||||
program TEXT,
|
||||
version TEXT,
|
||||
lib_version TEXT,
|
||||
test_station TEXT,
|
||||
source_file TEXT,
|
||||
import_date TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(wo_number, test_station)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_wo_number ON work_orders(wo_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_wo_station ON work_orders(test_station);
|
||||
|
||||
-- Work order lines table
|
||||
CREATE TABLE IF NOT EXISTS work_order_lines (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
wo_number TEXT NOT NULL,
|
||||
serial_number TEXT NOT NULL,
|
||||
status TEXT,
|
||||
model_number TEXT,
|
||||
ds_filename TEXT,
|
||||
test_date TEXT,
|
||||
test_time TEXT,
|
||||
test_station TEXT,
|
||||
UNIQUE(wo_number, serial_number, test_date, test_time)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_wol_wo ON work_order_lines(wo_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_wol_serial ON work_order_lines(serial_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_wol_model ON work_order_lines(model_number);
|
||||
|
||||
-- Grant permissions to app role
|
||||
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO testdatadb_app;
|
||||
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO testdatadb_app;
|
||||
@@ -0,0 +1,54 @@
|
||||
-- Test Data Database Schema
|
||||
-- SQLite database for storing and searching test records
|
||||
|
||||
-- Main test records table
|
||||
CREATE TABLE IF NOT EXISTS test_records (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
log_type TEXT NOT NULL, -- DSCLOG, 5BLOG, 7BLOG, 8BLOG, PWRLOG, SCTLOG, VASLOG, SHT
|
||||
model_number TEXT NOT NULL, -- DSCA38-1793, SCM5B30-01, etc.
|
||||
serial_number TEXT NOT NULL, -- 176923-1, 105840-2, etc.
|
||||
test_date TEXT NOT NULL, -- Test date (YYYY-MM-DD format)
|
||||
test_station TEXT, -- TS-1L, TS-3R, etc.
|
||||
overall_result TEXT, -- PASS/FAIL
|
||||
raw_data TEXT, -- Full original record
|
||||
source_file TEXT, -- Original file path
|
||||
import_date TEXT DEFAULT (datetime('now')),
|
||||
datasheet_exported_at TEXT DEFAULT NULL,
|
||||
forweb_exported_at TEXT DEFAULT NULL,
|
||||
UNIQUE(log_type, model_number, serial_number, test_date, test_station)
|
||||
);
|
||||
|
||||
-- Indexes for fast searching
|
||||
CREATE INDEX IF NOT EXISTS idx_serial ON test_records(serial_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_model ON test_records(model_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_date ON test_records(test_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_model_serial ON test_records(model_number, serial_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_result ON test_records(overall_result);
|
||||
CREATE INDEX IF NOT EXISTS idx_log_type ON test_records(log_type);
|
||||
|
||||
-- Full-text search virtual table
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS test_records_fts USING fts5(
|
||||
serial_number,
|
||||
model_number,
|
||||
raw_data,
|
||||
content='test_records',
|
||||
content_rowid='id'
|
||||
);
|
||||
|
||||
-- Triggers to keep FTS index in sync
|
||||
CREATE TRIGGER IF NOT EXISTS test_records_ai AFTER INSERT ON test_records BEGIN
|
||||
INSERT INTO test_records_fts(rowid, serial_number, model_number, raw_data)
|
||||
VALUES (new.id, new.serial_number, new.model_number, new.raw_data);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS test_records_ad AFTER DELETE ON test_records BEGIN
|
||||
INSERT INTO test_records_fts(test_records_fts, rowid, serial_number, model_number, raw_data)
|
||||
VALUES ('delete', old.id, old.serial_number, old.model_number, old.raw_data);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS test_records_au AFTER UPDATE ON test_records BEGIN
|
||||
INSERT INTO test_records_fts(test_records_fts, rowid, serial_number, model_number, raw_data)
|
||||
VALUES ('delete', old.id, old.serial_number, old.model_number, old.raw_data);
|
||||
INSERT INTO test_records_fts(rowid, serial_number, model_number, raw_data)
|
||||
VALUES (new.id, new.serial_number, new.model_number, new.raw_data);
|
||||
END;
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Parser for single-line CSV format (7BLOG)
|
||||
*
|
||||
* Format:
|
||||
* STAGE: MODEL,SERIAL,DATE,VERSION,CODE,VALUE1,VALUE2,...
|
||||
* Example:
|
||||
* FINAL: 7B21,87876-1,05-08-2013,1.984,0651945, 12, 9999, ...
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Parse a 7BLOG CSV file and extract test records
|
||||
* @param {string} filePath - Path to the DAT file
|
||||
* @param {string} testStation - Test station identifier
|
||||
* @returns {Array} Array of parsed records
|
||||
*/
|
||||
function parseCsvFile(filePath, testStation = null) {
|
||||
const records = [];
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n').map(l => l.trim());
|
||||
|
||||
for (const line of lines) {
|
||||
if (!line) continue;
|
||||
|
||||
// Match pattern: STAGE: MODEL,SERIAL,DATE,...
|
||||
const match = line.match(/^([A-Z-]+):\s*([^,]+),([^,]+),(\d{2}-\d{2}-\d{4}),(.*)$/);
|
||||
|
||||
if (match) {
|
||||
const [, stage, model, serial, dateStr, rest] = match;
|
||||
|
||||
// Parse date from MM-DD-YYYY to YYYY-MM-DD
|
||||
const [month, day, year] = dateStr.split('-');
|
||||
const testDate = `${year}-${month}-${day}`;
|
||||
|
||||
// Model number includes the stage prefix for 7B products
|
||||
const modelNumber = model.trim();
|
||||
|
||||
records.push({
|
||||
log_type: '7BLOG',
|
||||
model_number: modelNumber,
|
||||
serial_number: serial.trim(),
|
||||
test_date: testDate,
|
||||
test_station: testStation,
|
||||
overall_result: 'PASS', // 7BLOG entries are typically passing records
|
||||
raw_data: line,
|
||||
source_file: filePath
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error parsing ${filePath}: ${err.message}`);
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract test station from file path
|
||||
*/
|
||||
function extractTestStation(filePath) {
|
||||
const match = filePath.match(/TS-\d+[LR]/i);
|
||||
return match ? match[0].toUpperCase() : null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parseCsvFile,
|
||||
extractTestStation
|
||||
};
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Parser for multi-line DAT files (DSCLOG, 5BLOG, 8BLOG, PWRLOG, SCTLOG, VASLOG)
|
||||
*
|
||||
* Format:
|
||||
* "MODEL_NUMBER "
|
||||
* measurement1,measurement2,measurement3,measurement4,"PASS/FAIL"
|
||||
* ... (test data lines)
|
||||
* 0
|
||||
* "summary line 1"
|
||||
* ...
|
||||
* "SERIAL-NUM","MM-DD-YYYY"
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Parse a multi-line DAT file and extract test records
|
||||
* @param {string} filePath - Path to the DAT file
|
||||
* @param {string} logType - Type of log (DSCLOG, 5BLOG, etc.)
|
||||
* @param {string} testStation - Test station identifier (TS-1L, etc.)
|
||||
* @returns {Array} Array of parsed records
|
||||
*/
|
||||
function parseMultilineFile(filePath, logType, testStation = null) {
|
||||
const records = [];
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n').map(l => l.trim());
|
||||
|
||||
let currentRecord = [];
|
||||
let modelNumber = null;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
|
||||
// Skip empty lines
|
||||
if (!line) continue;
|
||||
|
||||
// Check if it's a serial/date line (format: "SERIAL","DATE")
|
||||
const serialDateMatch = line.match(/^"(\d+-\d+[A-Za-z]?)","(\d{2}-\d{2}-\d{4})"$/);
|
||||
|
||||
if (serialDateMatch) {
|
||||
// This is the end of a record
|
||||
const serialNumber = serialDateMatch[1];
|
||||
const dateStr = serialDateMatch[2];
|
||||
|
||||
if (modelNumber && currentRecord.length > 0) {
|
||||
// Parse date from MM-DD-YYYY to YYYY-MM-DD
|
||||
const [month, day, year] = dateStr.split('-');
|
||||
const testDate = `${year}-${month}-${day}`;
|
||||
|
||||
// Determine overall result from raw data
|
||||
const rawData = currentRecord.join('\n');
|
||||
const overallResult = determineResult(rawData);
|
||||
|
||||
records.push({
|
||||
log_type: logType,
|
||||
model_number: modelNumber.trim(),
|
||||
serial_number: serialNumber,
|
||||
test_date: testDate,
|
||||
test_station: testStation,
|
||||
overall_result: overallResult,
|
||||
raw_data: rawData,
|
||||
source_file: filePath
|
||||
});
|
||||
}
|
||||
|
||||
// Reset for next record
|
||||
currentRecord = [];
|
||||
modelNumber = null;
|
||||
}
|
||||
// Check if this is a model number line
|
||||
// Model numbers: single quoted string with product code (letters+numbers, possibly with dash)
|
||||
// Examples: "DSCA38-1793 ", "SCM5B30-01 ", "8B30-01 "
|
||||
else if (/^"[A-Z0-9]+[A-Z0-9-]*\s*"$/.test(line) && !line.includes(',') && !line.includes('PASS') && !line.includes('FAIL')) {
|
||||
// This is a model number line - start new record
|
||||
if (currentRecord.length > 0 && modelNumber) {
|
||||
// Previous record didn't have serial/date - skip it
|
||||
currentRecord = [];
|
||||
}
|
||||
modelNumber = line.replace(/"/g, '').trim();
|
||||
currentRecord.push(line);
|
||||
} else {
|
||||
// Add line to current record
|
||||
currentRecord.push(line);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error parsing ${filePath}: ${err.message}`);
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine overall PASS/FAIL result from raw data
|
||||
*/
|
||||
function determineResult(rawData) {
|
||||
const failCount = (rawData.match(/"FAIL/gi) || []).length;
|
||||
const passCount = (rawData.match(/"PASS/gi) || []).length;
|
||||
|
||||
if (failCount > 0) return 'FAIL';
|
||||
if (passCount > 0) return 'PASS';
|
||||
return 'UNKNOWN';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract test station from file path
|
||||
*/
|
||||
function extractTestStation(filePath) {
|
||||
const match = filePath.match(/TS-\d+[LR]/i);
|
||||
return match ? match[0].toUpperCase() : null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parseMultilineFile,
|
||||
extractTestStation
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Parser for SHT (Test Data Sheet) files
|
||||
*
|
||||
* Format:
|
||||
* DATAFORTH CORPORATION ...
|
||||
* ...
|
||||
* TEST DATA SHEET
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* Date: MM-DD-YYYY
|
||||
* Model: MODEL_NUMBER
|
||||
* SN: SERIAL-NUM
|
||||
*
|
||||
* Parameter Measured Value Specification Status
|
||||
* ======================= =============== ==================== ======
|
||||
* Supply Current 12.0 mA < 30 mA PASS
|
||||
* ...
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Parse an SHT file and extract the test record
|
||||
* @param {string} filePath - Path to the SHT file
|
||||
* @param {string} testStation - Test station identifier
|
||||
* @returns {Array} Array with single parsed record (or empty if parse fails)
|
||||
*/
|
||||
function parseShtFile(filePath, testStation = null) {
|
||||
const records = [];
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
let date = null;
|
||||
let model = null;
|
||||
let serial = null;
|
||||
let hasFailure = false;
|
||||
|
||||
for (const line of lines) {
|
||||
// Extract date
|
||||
const dateMatch = line.match(/^Date:\s*(\d{2}-\d{2}-\d{4})/);
|
||||
if (dateMatch) {
|
||||
const [month, day, year] = dateMatch[1].split('-');
|
||||
date = `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
// Extract model
|
||||
const modelMatch = line.match(/^Model:\s*(\S+)/);
|
||||
if (modelMatch) {
|
||||
model = modelMatch[1].trim();
|
||||
}
|
||||
|
||||
// Extract serial number
|
||||
const snMatch = line.match(/^SN:\s*(\S+)/);
|
||||
if (snMatch) {
|
||||
serial = snMatch[1].trim();
|
||||
}
|
||||
|
||||
// Check for FAIL status
|
||||
if (/\bFAIL\b/i.test(line)) {
|
||||
hasFailure = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (date && model && serial) {
|
||||
records.push({
|
||||
log_type: 'SHT',
|
||||
model_number: model,
|
||||
serial_number: serial,
|
||||
test_date: date,
|
||||
test_station: testStation,
|
||||
overall_result: hasFailure ? 'FAIL' : 'PASS',
|
||||
raw_data: content,
|
||||
source_file: filePath
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error parsing ${filePath}: ${err.message}`);
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract test station from file path
|
||||
*/
|
||||
function extractTestStation(filePath) {
|
||||
const match = filePath.match(/TS-\d+[LR]/i);
|
||||
return match ? match[0].toUpperCase() : null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parseShtFile,
|
||||
extractTestStation
|
||||
};
|
||||
@@ -0,0 +1,487 @@
|
||||
/**
|
||||
* Spec Reader - Parses QuickBASIC binary DAT spec files
|
||||
*
|
||||
* Reads model specification data from 4 product family DAT files:
|
||||
* 5BMAIN.DAT (SCM5B family, 160 bytes/record)
|
||||
* 8BMAIN.DAT (8B family, 163 bytes/record)
|
||||
* DSCOUT.DAT (DSCA family, 163 bytes/record)
|
||||
* SCTMAIN.DAT (DSCT family, 121 bytes/record)
|
||||
*
|
||||
* These are QuickBASIC random-access files using TYPE (struct) records.
|
||||
* All values are little-endian: SINGLE = IEEE 754 float (4 bytes),
|
||||
* INTEGER = signed 16-bit (2 bytes), STRING * N = fixed-width ASCII.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Default spec data directory
|
||||
const DEFAULT_SPEC_DIR = path.join(__dirname, '..', 'specdata');
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Binary read helpers
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
function readString(buf, offset, length) {
|
||||
return buf.toString('ascii', offset, offset + length).replace(/\0/g, '').trim();
|
||||
}
|
||||
|
||||
function readSingle(buf, offset) {
|
||||
return buf.readFloatLE(offset);
|
||||
}
|
||||
|
||||
function readInteger(buf, offset) {
|
||||
return buf.readInt16LE(offset);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// TYPE definitions (field name, type, size)
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
const FIELD_TYPES = {
|
||||
STRING17: { size: 17, read: (buf, off) => readString(buf, off, 17) },
|
||||
STRING9: { size: 9, read: (buf, off) => readString(buf, off, 9) },
|
||||
STRING15: { size: 15, read: (buf, off) => readString(buf, off, 15) },
|
||||
STRING14: { size: 14, read: (buf, off) => readString(buf, off, 14) },
|
||||
STRING13: { size: 13, read: (buf, off) => readString(buf, off, 13) },
|
||||
STRING7: { size: 7, read: (buf, off) => readString(buf, off, 7) },
|
||||
SINGLE: { size: 4, read: (buf, off) => readSingle(buf, off) },
|
||||
INTEGER: { size: 2, read: (buf, off) => readInteger(buf, off) },
|
||||
};
|
||||
|
||||
const S15 = 'STRING15';
|
||||
const S14 = 'STRING14';
|
||||
const S13 = 'STRING13';
|
||||
const S7 = 'STRING7';
|
||||
const SNG = 'SINGLE';
|
||||
const INT = 'INTEGER';
|
||||
|
||||
// SCM5B: 160 bytes/record
|
||||
const SCM5B_FIELDS = [
|
||||
['MODNAME', S15], ['SENTYPE', S7],
|
||||
['ISMAXNEXCL', SNG], ['ISMAXFEXCL', SNG], ['OUTRES', SNG],
|
||||
['MININ', SNG], ['MAXIN', SNG], ['IEXC', SNG],
|
||||
['RCONV', SNG], ['MINOUT', SNG], ['MAXOUT', SNG],
|
||||
['OSCALPT', SNG], ['GNCALPT', SNG], ['CALTOL', SNG],
|
||||
['VEXC', SNG], ['VEXCACC', SNG], ['EXCLOAD', SNG],
|
||||
['EXCLOADREG', SNG], ['EXCIMAX', SNG], ['LINEAR', SNG],
|
||||
['ACCURACY', SNG], ['TESTFREQ', SNG], ['ATTEN', SNG],
|
||||
['ATTENTOL', SNG], ['STEPRMIN', SNG], ['STEPRMAX', SNG],
|
||||
['PSS', SNG], ['OUTNOISE', SNG], ['INPUTRES', SNG],
|
||||
['VOPENINMIN', SNG], ['VOPENINMAX', SNG], ['LEADRERR', SNG],
|
||||
['LINEARIZED', INT], ['OSCALIN', SNG], ['GNCALIN', SNG],
|
||||
['BANDWIDTH', SNG], ['IMATCHTOL', SNG],
|
||||
];
|
||||
|
||||
// 8B: 163 bytes/record (no OUTRES, has OUTSIGTYPE)
|
||||
const B8_FIELDS = [
|
||||
['MODNAME', S15], ['SENTYPE', S7],
|
||||
['ISMAXNEXCL', SNG], ['ISMAXFEXCL', SNG],
|
||||
['MININ', SNG], ['MAXIN', SNG], ['IEXC', SNG],
|
||||
['RCONV', SNG], ['OUTSIGTYPE', S7],
|
||||
['MINOUT', SNG], ['MAXOUT', SNG],
|
||||
['OSCALPT', SNG], ['GNCALPT', SNG], ['CALTOL', SNG],
|
||||
['VEXC', SNG], ['VEXCACC', SNG], ['EXCLOAD', SNG],
|
||||
['EXCLOADREG', SNG], ['EXCIMAX', SNG], ['LINEAR', SNG],
|
||||
['ACCURACY', SNG], ['TESTFREQ', SNG], ['ATTEN', SNG],
|
||||
['ATTENTOL', SNG], ['STEPRMIN', SNG], ['STEPRMAX', SNG],
|
||||
['PSS', SNG], ['OUTNOISE', SNG], ['INPUTRES', SNG],
|
||||
['VOPENINMIN', SNG], ['VOPENINMAX', SNG], ['LEADRERR', SNG],
|
||||
['LINEARIZED', INT], ['OSCALIN', SNG], ['GNCALIN', SNG],
|
||||
['BANDWIDTH', SNG], ['IMATCHTOL', SNG],
|
||||
];
|
||||
|
||||
// DSCA: 163 bytes/record
|
||||
const DSCA_FIELDS = [
|
||||
['MODNAME', S13], ['SENTYPE', S7],
|
||||
['ISMAXNL', SNG], ['ISMAXFL', SNG],
|
||||
['MININ', SNG], ['MAXIN', SNG], ['RCONV', SNG],
|
||||
['MINOUT', SNG], ['MAXOUT', SNG], ['OUTSIGTYPE', S7],
|
||||
['OSCALIN', SNG], ['GNCALIN', SNG], ['CALTOL', SNG],
|
||||
['LOAD1', SNG], ['LINEAR1', SNG], ['ACCURACY1', SNG],
|
||||
['LOAD2', SNG], ['LINEAR2', SNG], ['ACCURACY2', SNG],
|
||||
['LOAD3', SNG], ['LINEAR3', SNG], ['ACCURACY3', SNG],
|
||||
['BANDWIDTH', SNG], ['TESTFREQ', SNG], ['ATTEN', SNG],
|
||||
['ATTENTOL', SNG], ['STEPRMIN', SNG], ['STEPRMAX', SNG],
|
||||
['PSS', SNG], ['OUTNOISE', SNG], ['INPUTRES', SNG],
|
||||
['COMPLIANCE', SNG], ['MAXLOAD', SNG], ['ILIMIT', SNG],
|
||||
['PERCOVER', SNG], ['MINVS', SNG], ['MAXVS', SNG],
|
||||
];
|
||||
|
||||
// DSCT: 121 bytes/record (uses INTEGER for some fields)
|
||||
const DSCT_FIELDS = [
|
||||
['MODNAME', S14], ['SENTYPE', S7],
|
||||
['MININ', SNG], ['MAXIN', SNG],
|
||||
['IEXCMFS', SNG], ['IEXCPFS', SNG],
|
||||
['RCONV', SNG], ['MINOUT', SNG], ['MAXOUT', SNG],
|
||||
['OSCALPT', SNG], ['GNCALPT', SNG],
|
||||
['LINEAR', SNG], ['ACCURACY', SNG],
|
||||
['TESTFREQ', INT], ['ATTEN', INT], ['ATTENTOL', INT],
|
||||
['STEPRMIN', SNG], ['STEPRMAX', SNG],
|
||||
['OUTNOISE', SNG], ['INPUTRES', SNG],
|
||||
['IOPENTC', SNG], ['LEADRERR', SNG],
|
||||
['LINEARIZED', INT], ['OSCALIN', SNG], ['GNCALIN', SNG],
|
||||
['BANDWIDTH', SNG], ['IMATCHTOL', SNG],
|
||||
['CALTOL', SNG], ['VSEN', SNG],
|
||||
];
|
||||
|
||||
const S9 = 'STRING9';
|
||||
|
||||
// SCM5B45: 119 bytes/record (frequency/counter modules)
|
||||
const SCM5B45_FIELDS = [
|
||||
['MODNAME', S9],
|
||||
['MININ', SNG], ['MAXIN', SNG], ['MINOUT', SNG], ['MAXOUT', SNG],
|
||||
['ZHYSAMPL', SNG], ['ZHYSLIM', SNG], ['TTLHYSAMPL', SNG],
|
||||
['TTLLIMHI', SNG], ['TTLLIMLO', SNG], ['MINPW', SNG],
|
||||
['OSCALIN', SNG], ['GNCALIN', SNG], ['CALTOL', SNG],
|
||||
['LINEAR', SNG], ['ACCURACY', SNG],
|
||||
['TESTFREQ', INT], ['ATTEN', INT], ['ATTENTOL', INT],
|
||||
['STEPRMIN', SNG], ['STEPRMAX', SNG],
|
||||
['ISMAX', SNG], ['PSS', SNG],
|
||||
['NOISEMFS', SNG], ['NOISETESTPT', SNG], ['NOISEPFS', SNG],
|
||||
['OUTRES', SNG], ['EXCVOLT', SNG],
|
||||
['EXCTOLNL', SNG], ['EXCTOLL', SNG],
|
||||
];
|
||||
|
||||
// SCM5B48: 264 bytes/record (multi-bandwidth modules)
|
||||
const SCM5B48_FIELDS = [
|
||||
['MODNAME', S15], ['SENTYPE', S7],
|
||||
['ISMAXNEXCL', SNG], ['ISMAXFEXCL', SNG], ['OUTRES', SNG],
|
||||
['MININ', SNG], ['MAXIN', SNG],
|
||||
['MININ1', SNG], ['MAXIN1', SNG],
|
||||
['MININ2', SNG], ['MAXIN2', SNG],
|
||||
['MININ3', SNG], ['MAXIN3', SNG],
|
||||
['IEXC', SNG], ['IEXC1', SNG], ['IEXC2', SNG],
|
||||
['RCONV', SNG], ['MINOUT', SNG], ['MAXOUT', SNG],
|
||||
['OSCALPT', SNG], ['GNCALPT', SNG], ['CALTOL', SNG],
|
||||
['VEXC', SNG], ['VEXCACC', SNG], ['EXCLOAD', SNG],
|
||||
['EXCLOADREG', SNG], ['EXCIMAX', SNG],
|
||||
['LINEAR', SNG], ['ACCURACY', SNG],
|
||||
['TESTFREQ', SNG], ['TESTFREQ1', SNG], ['TESTFREQ2', SNG], ['TESTFREQ3', SNG], ['TESTFREQ4', SNG],
|
||||
['ATTEN', SNG], ['ATTEN1', SNG], ['ATTEN2', SNG], ['ATTEN3', SNG], ['ATTEN4', SNG],
|
||||
['ATTENTOL', SNG],
|
||||
['STEPRMIN', SNG], ['STEPRMAX', SNG],
|
||||
['PSS', SNG], ['PSS1', SNG], ['PSS2', SNG], ['PSS3', SNG],
|
||||
['OUTNOISE', SNG], ['OUTNOISE1', SNG], ['OUTNOISE2', SNG], ['OUTNOISE3', SNG],
|
||||
['INPUTRES', SNG], ['VOPENINMIN', SNG], ['VOPENINMAX', SNG], ['LEADRERR', SNG],
|
||||
['LINEARIZED', INT],
|
||||
['OSCALIN', SNG], ['GNCALIN', SNG],
|
||||
['BANDWIDTH', SNG], ['BANDWIDTH1', SNG], ['BANDWIDTH2', SNG], ['BANDWIDTH3', SNG], ['BANDWIDTH4', SNG],
|
||||
['IMATCHTOL', SNG],
|
||||
];
|
||||
|
||||
// SCM5B49: 93 bytes/record (sample & hold modules)
|
||||
const SCM5B49_FIELDS = [
|
||||
['MODNAME', S9],
|
||||
['MININ', SNG], ['MAXIN', SNG], ['MINOUT', SNG], ['MAXOUT', SNG],
|
||||
['MAXSUPPLYNL', SNG], ['MAXSUPPLYFL', SNG], ['LIMITOUT', SNG], ['POWERSEN', SNG],
|
||||
['TESTFREQ', INT], ['ATTEN', INT],
|
||||
['LINEAR0MA', SNG], ['LINEAR50MA', SNG],
|
||||
['ACCURACY0MA', SNG], ['ACCURACY50MA', SNG],
|
||||
['STEPRMIN', SNG], ['STEPRMAX', SNG],
|
||||
['NOISEOUT', SNG], ['QINJECT', SNG],
|
||||
['INPUTRES', SNG], ['ACQLIM', SNG],
|
||||
['DROOP', SNG], ['PERCOVER', SNG],
|
||||
];
|
||||
|
||||
// DSCA (TSTDIN1B variant, for DSCMAIN4.DAT): 159 bytes/record
|
||||
const DSCA_DIN_FIELDS = [
|
||||
['MODNAME', S13], ['SENTYPE', S7],
|
||||
['ISMAXNEXCL', SNG], ['ISMAXFEXCL', SNG],
|
||||
['MININ', SNG], ['MAXIN', SNG],
|
||||
['IEXCPFS', SNG], ['IEXCMFS', SNG],
|
||||
['RCONV', SNG], ['OUTSIGTYPE', S7],
|
||||
['MINOUT', SNG], ['MAXOUT', SNG],
|
||||
['OSCALPT', SNG], ['GNCALPT', SNG], ['CALTOL', SNG],
|
||||
['VEXC', SNG], ['VEXCACC', SNG], ['EXCLOAD', SNG],
|
||||
['EXCLOADREG', SNG], ['EXCIMAX', SNG],
|
||||
['LINEAR', SNG], ['ACCURACY', SNG],
|
||||
['TESTFREQ', INT], ['ATTEN', INT], ['ATTENTOL', INT],
|
||||
['STEPRMIN', SNG], ['STEPRMAX', SNG],
|
||||
['PSS', SNG], ['OUTNOISE', SNG], ['INPUTRES', SNG],
|
||||
['OPENTC', SNG], ['LEADRERR', SNG],
|
||||
['LINEARIZED', INT], ['OSCALIN', SNG], ['GNCALIN', SNG],
|
||||
['BANDWIDTH', SNG], ['MINVS', SNG], ['MAXVS', SNG],
|
||||
];
|
||||
|
||||
// SCM7B: 170 bytes/record
|
||||
const S17 = 'STRING17';
|
||||
const SCM7B_FIELDS = [
|
||||
['MODNAME', S17], ['SENTYPE', S7],
|
||||
['MINVS', SNG], ['NOMVS', SNG], ['MAXVS', SNG],
|
||||
['VLIM', SNG], ['ILIM', SNG], ['PE', SNG],
|
||||
['ISMAXNEXCL', SNG],
|
||||
['MININ', SNG], ['MAXIN', SNG],
|
||||
['MINOUT', SNG], ['MAXOUT', SNG],
|
||||
['IEXC', SNG], ['EXCIMIN', SNG], ['EXCIMAX', SNG],
|
||||
['LEADRERR', SNG], ['RCONV', SNG],
|
||||
['OSCALPT', SNG], ['GNCALPT', SNG], ['CALTOL', SNG],
|
||||
['ISMAXFEXCL', SNG],
|
||||
['VEXC', SNG], ['VEXCLO', SNG], ['VEXCHI', SNG],
|
||||
['LOOPIMAX', SNG],
|
||||
['LINEAR', SNG], ['ACCURACY', SNG], ['PSS', SNG],
|
||||
['TESTFREQ', INT], ['ATTEN', INT], ['ATTENTOL', INT],
|
||||
['STEPRESP', SNG], ['STEPTOL', SNG],
|
||||
['OUTNOISERMS', SNG], ['OUTNOISEVPK', SNG],
|
||||
['INPUTRES', SNG], ['VOPENTC', SNG],
|
||||
['CJCACC', SNG], ['IBIAS', SNG],
|
||||
];
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Record size calculation
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
function calcRecordSize(fields) {
|
||||
let size = 0;
|
||||
for (const [, type] of fields) {
|
||||
size += FIELD_TYPES[type].size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Parse a single record from a buffer
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
function parseRecord(buf, offset, fields) {
|
||||
const record = {};
|
||||
let pos = offset;
|
||||
for (const [name, type] of fields) {
|
||||
const ft = FIELD_TYPES[type];
|
||||
record[name] = ft.read(buf, pos);
|
||||
pos += ft.size;
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Parse an entire DAT file into an array of records
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
function parseDatFile(filePath, fields) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.error(`Spec file not found: ${filePath}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
const buf = fs.readFileSync(filePath);
|
||||
const recordSize = calcRecordSize(fields);
|
||||
const numRecords = Math.floor(buf.length / recordSize);
|
||||
const records = [];
|
||||
|
||||
for (let i = 0; i < numRecords; i++) {
|
||||
const offset = i * recordSize;
|
||||
if (offset + recordSize > buf.length) break;
|
||||
|
||||
const record = parseRecord(buf, offset, fields);
|
||||
|
||||
// Skip records with empty, placeholder, or corrupted model names
|
||||
const modname = record.MODNAME;
|
||||
if (!modname || modname.length === 0) continue;
|
||||
// Skip if model name contains non-alphanumeric characters (except dash)
|
||||
if (!/^[A-Za-z0-9-]+$/.test(modname)) continue;
|
||||
// Skip placeholder entries
|
||||
if (/^[XYZ]+$/.test(modname) || /^ZZZZ/.test(modname)) continue;
|
||||
// Skip if MODNAME doesn't start with a known product prefix
|
||||
const upper = modname.toUpperCase();
|
||||
if (!upper.match(/^(SCM5B|5B|SCM7B|7B|8B|DSCA|DSCT|SCT|BOGUS)/)) continue;
|
||||
|
||||
records.push(record);
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Family configuration
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
const FAMILIES = {
|
||||
SCM5B: {
|
||||
file: '5BMAIN.DAT',
|
||||
fields: SCM5B_FIELDS,
|
||||
family: 'SCM5B',
|
||||
logType: '5BLOG',
|
||||
},
|
||||
B8: {
|
||||
file: '8BMAIN.DAT',
|
||||
fields: B8_FIELDS,
|
||||
family: '8B',
|
||||
logType: '8BLOG',
|
||||
},
|
||||
DSCA: {
|
||||
file: 'DSCOUT.DAT',
|
||||
fields: DSCA_FIELDS,
|
||||
family: 'DSCA',
|
||||
logType: 'DSCLOG',
|
||||
},
|
||||
DSCT: {
|
||||
file: 'SCTMAIN.DAT',
|
||||
fields: DSCT_FIELDS,
|
||||
family: 'DSCT',
|
||||
logType: 'SCTLOG',
|
||||
},
|
||||
DSCA_DIN: {
|
||||
file: 'DSCMAIN4.DAT',
|
||||
fields: DSCA_DIN_FIELDS,
|
||||
family: 'DSCA',
|
||||
logType: 'DSCLOG',
|
||||
},
|
||||
SCM5B45: {
|
||||
file: '5B45DATA.DAT',
|
||||
fields: SCM5B45_FIELDS,
|
||||
family: 'SCM5B',
|
||||
logType: '5BLOG',
|
||||
},
|
||||
SCM5B48: {
|
||||
file: 'DB5B48.DAT',
|
||||
fields: SCM5B48_FIELDS,
|
||||
family: 'SCM5B',
|
||||
logType: '5BLOG',
|
||||
},
|
||||
SCM5B49: {
|
||||
file: '5B49_2.DAT',
|
||||
fields: SCM5B49_FIELDS,
|
||||
family: 'SCM5B',
|
||||
logType: '5BLOG',
|
||||
},
|
||||
SCM7B: {
|
||||
file: '7BMAIN.DAT',
|
||||
fields: SCM7B_FIELDS,
|
||||
family: 'SCM7B',
|
||||
logType: '7BLOG',
|
||||
},
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Main API: load all specs into a lookup map
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Load all model specs from binary DAT files.
|
||||
* @param {string} specDir - Directory containing the DAT files
|
||||
* @returns {Map<string, object>} Map of model_number -> spec record (with _family added)
|
||||
*/
|
||||
function loadAllSpecs(specDir) {
|
||||
specDir = specDir || DEFAULT_SPEC_DIR;
|
||||
const specMap = new Map();
|
||||
|
||||
for (const [familyKey, config] of Object.entries(FAMILIES)) {
|
||||
const filePath = path.join(specDir, config.file);
|
||||
const records = parseDatFile(filePath, config.fields);
|
||||
|
||||
for (const record of records) {
|
||||
record._family = config.family;
|
||||
record._logType = config.logType;
|
||||
// Normalize model name for lookup (trim, uppercase)
|
||||
const key = record.MODNAME.toUpperCase().trim();
|
||||
specMap.set(key, record);
|
||||
}
|
||||
|
||||
console.log(`[SPEC] Loaded ${records.length} models from ${config.file} (${config.family})`);
|
||||
}
|
||||
|
||||
console.log(`[SPEC] Total models loaded: ${specMap.size}`);
|
||||
return specMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up specs for a model number.
|
||||
* Tries exact match, then common prefix variations (SCM5B <-> 5B, DSCA <-> DSC).
|
||||
* @param {Map} specMap - Spec map from loadAllSpecs()
|
||||
* @param {string} modelNumber - Model number to look up
|
||||
* @returns {object|null} Spec record or null
|
||||
*/
|
||||
function getSpecs(specMap, modelNumber) {
|
||||
if (!modelNumber) return null;
|
||||
const key = modelNumber.toUpperCase().trim();
|
||||
|
||||
// Exact match
|
||||
if (specMap.has(key)) return specMap.get(key);
|
||||
|
||||
// Try adding/removing SCM prefix: "5B41-03" <-> "SCM5B41-03"
|
||||
if (key.startsWith('SCM5B')) {
|
||||
const short = key.replace('SCM5B', '5B');
|
||||
if (specMap.has(short)) return specMap.get(short);
|
||||
} else if (key.startsWith('5B')) {
|
||||
const full = 'SCM' + key;
|
||||
if (specMap.has(full)) return specMap.get(full);
|
||||
}
|
||||
|
||||
// Try adding/removing SCM prefix for 7B
|
||||
if (key.startsWith('SCM7B')) {
|
||||
const short = key.replace('SCM7B', '7B');
|
||||
if (specMap.has(short)) return specMap.get(short);
|
||||
} else if (key.startsWith('7B')) {
|
||||
const full = 'SCM' + key;
|
||||
if (specMap.has(full)) return specMap.get(full);
|
||||
}
|
||||
|
||||
// Try DSCA variations
|
||||
if (key.startsWith('DSCA')) {
|
||||
// Some specs stored without the 'A'
|
||||
const short = key.replace('DSCA', 'DSC');
|
||||
if (specMap.has(short)) return specMap.get(short);
|
||||
}
|
||||
|
||||
// Try partial match on model base (before any suffix like C, D)
|
||||
// e.g., "DSCA30-05C" -> try "DSCA30-05"
|
||||
const baseMatch = key.match(/^(.+?)([A-Z])$/);
|
||||
if (baseMatch) {
|
||||
const base = baseMatch[1];
|
||||
if (specMap.has(base)) return specMap.get(base);
|
||||
// Also try with prefix variations
|
||||
if (base.startsWith('SCM5B')) {
|
||||
const short = base.replace('SCM5B', '5B');
|
||||
if (specMap.has(short)) return specMap.get(short);
|
||||
} else if (base.startsWith('5B')) {
|
||||
if (specMap.has('SCM' + base)) return specMap.get('SCM' + base);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine product family from model number string
|
||||
*/
|
||||
function getFamily(modelNumber) {
|
||||
if (!modelNumber) return null;
|
||||
const m = modelNumber.toUpperCase();
|
||||
if (m.startsWith('SCM5B') || m.startsWith('5B')) return 'SCM5B';
|
||||
if (m.startsWith('SCM7B') || m.startsWith('7B')) return 'SCM7B';
|
||||
if (m.startsWith('8B')) return '8B';
|
||||
if (m.startsWith('DSCA')) return 'DSCA';
|
||||
if (m.startsWith('DSCT') || m.startsWith('SCT')) return 'DSCT';
|
||||
return null;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// CLI: test the parser
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
if (require.main === module) {
|
||||
const specDir = process.argv[2] || DEFAULT_SPEC_DIR;
|
||||
console.log(`Loading specs from: ${specDir}\n`);
|
||||
|
||||
const specMap = loadAllSpecs(specDir);
|
||||
|
||||
// Print a few examples from each family
|
||||
const examples = {};
|
||||
for (const [key, spec] of specMap) {
|
||||
const fam = spec._family;
|
||||
if (!examples[fam]) examples[fam] = [];
|
||||
if (examples[fam].length < 3) {
|
||||
examples[fam].push(spec);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [fam, specs] of Object.entries(examples)) {
|
||||
console.log(`\n--- ${fam} Examples ---`);
|
||||
for (const s of specs) {
|
||||
console.log(` ${s.MODNAME}: SENTYPE=${s.SENTYPE}, MININ=${s.MININ}, MAXIN=${s.MAXIN}, MINOUT=${s.MINOUT}, MAXOUT=${s.MAXOUT}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { loadAllSpecs, getSpecs, getFamily, FAMILIES };
|
||||
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* Work Order Report Parser
|
||||
*
|
||||
* Parses the TXT work order status reports from TS-XX/Reports/ folders.
|
||||
*
|
||||
* Format:
|
||||
* ===================================================================
|
||||
* WO#: 179257
|
||||
* Date: 03-27-2026
|
||||
* Work order status file for work order #: 179257
|
||||
* Program: TEST8B1D.EXE
|
||||
* Version: B.19 2023.08.02 JL
|
||||
* Lib. Ver.: B.09 2019.02.08 MR
|
||||
* -------------------------------------------------------------------
|
||||
* Status Serial# DS File Name Model Date Time
|
||||
* -------- --------- ------------ ------------- ---------- --------
|
||||
* PASS 179257-1 H9257-1.TXT 8B47K-05 03-27-2026 10:25:56
|
||||
* FAIL<<<< 179257-12 8B47K-05 03-27-2026 11:01:09
|
||||
* ...
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Extract test station from file path (e.g., C:\Shares\test\TS-4L\Reports\179257.TXT -> TS-4L)
|
||||
*/
|
||||
function extractStation(filePath) {
|
||||
const match = filePath.match(/[\\\/](TS-[^\\\/]+)[\\\/]/i);
|
||||
return match ? match[1].toUpperCase() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract work order number from filename (e.g., 179257.TXT -> 179257)
|
||||
*/
|
||||
function extractWoFromFilename(filePath) {
|
||||
const base = path.basename(filePath, path.extname(filePath));
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a work order report TXT file
|
||||
* @param {string} filePath - Path to the report file
|
||||
* @returns {object} Parsed work order with header and lines
|
||||
*/
|
||||
function parseWoReport(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split(/\r?\n/);
|
||||
|
||||
const result = {
|
||||
wo_number: null,
|
||||
wo_date: null,
|
||||
program: null,
|
||||
version: null,
|
||||
lib_version: null,
|
||||
station: extractStation(filePath),
|
||||
source_file: filePath,
|
||||
lines: [], // test result lines
|
||||
ds_files: [], // datasheet files listed at bottom
|
||||
};
|
||||
|
||||
let inHeader = true;
|
||||
let inDsList = false;
|
||||
|
||||
for (const line of lines) {
|
||||
const t = line.trim();
|
||||
|
||||
// Parse header fields
|
||||
const woMatch = t.match(/^WO#:\s*(\S+)/);
|
||||
if (woMatch) {
|
||||
result.wo_number = woMatch[1];
|
||||
continue;
|
||||
}
|
||||
|
||||
const dateMatch = t.match(/^Date:\s*(\d{2}-\d{2}-\d{4})/);
|
||||
if (dateMatch) {
|
||||
const [month, day, year] = dateMatch[1].split('-');
|
||||
result.wo_date = `${year}-${month}-${day}`;
|
||||
continue;
|
||||
}
|
||||
|
||||
const progMatch = t.match(/^Program:\s*(\S+)/);
|
||||
if (progMatch) {
|
||||
result.program = progMatch[1];
|
||||
continue;
|
||||
}
|
||||
|
||||
const verMatch = t.match(/^Version:\s*(.+)/);
|
||||
if (verMatch) {
|
||||
result.version = verMatch[1].trim();
|
||||
continue;
|
||||
}
|
||||
|
||||
const libMatch = t.match(/^Lib\. Ver\.:\s*(.+)/);
|
||||
if (libMatch) {
|
||||
result.lib_version = libMatch[1].trim();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detect separator lines
|
||||
if (t.match(/^-{20,}$/)) {
|
||||
inHeader = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip header row
|
||||
if (t.startsWith('Status') && t.includes('Serial#')) continue;
|
||||
|
||||
// Detect datasheet file list section
|
||||
if (t.includes('datasheet files actually created')) {
|
||||
inDsList = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inDsList) {
|
||||
if (t.match(/^-+$/)) continue;
|
||||
if (t.match(/^\S+\.TXT$/i)) {
|
||||
result.ds_files.push(t);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse test result lines
|
||||
// PASS 179257-1 H9257-1.TXT 8B47K-05 03-27-2026 10:25:56
|
||||
// FAIL<<<< 179257-12 8B47K-05 03-27-2026 11:01:09
|
||||
if (!inHeader && t.length > 0) {
|
||||
const passMatch = t.match(/^(PASS)\s+(\S+)\s+(\S+\.TXT)\s+(\S+)\s+(\d{2}-\d{2}-\d{4})\s+(\d{2}:\d{2}:\d{2})/i);
|
||||
const failMatch = t.match(/^(FAIL[<]*)\s+(\S+)\s+(\S+)\s+(\d{2}-\d{2}-\d{4})\s+(\d{2}:\d{2}:\d{2})/i);
|
||||
|
||||
if (passMatch) {
|
||||
const [, status, serial, dsFile, model, date, time] = passMatch;
|
||||
const [month, day, year] = date.split('-');
|
||||
result.lines.push({
|
||||
status: 'PASS',
|
||||
serial_number: serial,
|
||||
ds_filename: dsFile,
|
||||
model_number: model,
|
||||
test_date: `${year}-${month}-${day}`,
|
||||
test_time: time,
|
||||
});
|
||||
} else if (failMatch) {
|
||||
const [, status, serial, model, date, time] = failMatch;
|
||||
const [month, day, year] = date.split('-');
|
||||
result.lines.push({
|
||||
status: 'FAIL',
|
||||
serial_number: serial,
|
||||
ds_filename: null,
|
||||
model_number: model,
|
||||
test_date: `${year}-${month}-${day}`,
|
||||
test_time: time,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to filename for WO# if not found in content
|
||||
if (!result.wo_number) {
|
||||
result.wo_number = extractWoFromFilename(filePath);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = { parseWoReport, extractStation, extractWoFromFilename };
|
||||
@@ -0,0 +1,775 @@
|
||||
/**
|
||||
* Exact-Match Datasheet Formatter
|
||||
*
|
||||
* Generates TXT datasheets matching the original QuickBASIC DATASHEETWRITE output.
|
||||
* Requires a DB record (with raw_data) and model specs from spec-reader.
|
||||
*/
|
||||
|
||||
const { getFamily } = require('../parsers/spec-reader');
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// DATA LINES: parameter names and units per family
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
const DATA_LINES = {
|
||||
SCM5B: [
|
||||
['Supply Current, Nom', 'mA'], // 1
|
||||
['Supply Current, Max', 'mA'], // 2
|
||||
['Exc. Current #1', 'uA'], // 3
|
||||
['Exc. Current #2', 'uA'], // 4
|
||||
['Exc. Current Match', 'uA'], // 5
|
||||
['Output Resistance', 'ohms'], // 6
|
||||
['CJC Gain', 'uV/C'], // 7
|
||||
['Exc. Voltage', 'V'], // 8
|
||||
['Exc. Load Reg.', 'ppm/mA'], // 9
|
||||
['Vout Reg. w/ Load', '%'], // 10
|
||||
['Exc. Current Limit', 'mA'], // 11
|
||||
['Linearity', '%'], // 12
|
||||
['Accuracy', '%'], // 13
|
||||
['Lead R Effect', 'C/ohm'], // 14
|
||||
['Supply Sensitivity', 'uV/%'], // 15
|
||||
['Input Resistance', 'Mohms'], // 16
|
||||
['Open Input Response', 'V'], // 17
|
||||
['Frequency Response', 'dB'], // 18
|
||||
['Step Response', '%'], // 19
|
||||
['Output Noise', 'uVrms'], // 20
|
||||
['Over-range Response', 'V'], // 21
|
||||
],
|
||||
'8B': [
|
||||
['Supply Current, Nom', 'mA'],
|
||||
['Supply Current, Max', 'mA'],
|
||||
['Exc. Current #1', 'uA'],
|
||||
['Exc. Current #2', 'uA'],
|
||||
['Exc. Current Match', 'uA'],
|
||||
['Output Resistance', 'ohms'],
|
||||
['CJC Gain', 'uV/C'],
|
||||
['Exc. Voltage', 'V'],
|
||||
['Exc. Load Reg.', 'ppm/mA'],
|
||||
['Vout Reg. w/ Load', '%'],
|
||||
['Exc. Current Limit', 'mA'],
|
||||
['Linearity', '%'],
|
||||
['Accuracy', '%'],
|
||||
['Lead R Effect', 'C/ohm'],
|
||||
['Supply Sensitivity', 'ppm/%'],
|
||||
['Input Resistance', 'Mohms'],
|
||||
['Open Input Response', 'V'],
|
||||
['Frequency Response', 'dB'],
|
||||
['Step Response', '%'],
|
||||
['Output Noise', 'uVrms'],
|
||||
['Over-range Response', 'V'],
|
||||
],
|
||||
DSCA: [
|
||||
['Supply Current, Nom', 'mA'],
|
||||
['Supply Current @ Max Load', 'mA'],
|
||||
['Linearity, 0mA Load', '%'],
|
||||
['Accuracy, 0mA Load', '%'],
|
||||
['Linearity, 5mA Load', '%'],
|
||||
['Accuracy, 5mA Load', '%'],
|
||||
['Linearity, 50mA Load', '%'],
|
||||
['Accuracy, 50mA Load', '%'],
|
||||
['Positive Current Limit', 'mA'],
|
||||
['Negative Current Limit', 'mA'],
|
||||
['Overrange', '%'],
|
||||
['Power Supply Sensitivity', '%/%'],
|
||||
['Input Resistance', 'Mohms'],
|
||||
['Frequency Response', 'dB'],
|
||||
['Step Response', '%'],
|
||||
['Output Noise', ''],
|
||||
['Compliance', '%'],
|
||||
['Accuracy @ 5 ohm load', '%'],
|
||||
],
|
||||
SCM7B: [
|
||||
['Supply Current', 'mA'], // 1
|
||||
['Supply Current w/ Load', 'mA'], // 2
|
||||
['Bias Current', 'nA'], // 3
|
||||
['Input Resistance', 'kohms'], // 4
|
||||
['Offset Calibration', 'mV'], // 5
|
||||
['Gain Calibration', 'mV'], // 6
|
||||
['Linearity/Conformity', '%'], // 7
|
||||
['Accuracy', '%'], // 8
|
||||
['VLoop @ 0 mA (Vs = 18V)', 'V'], // 9
|
||||
[' (Vs = 35V)', 'V'], // 10
|
||||
['VLoop @ 4 mA (Vs = 18V)', 'V'], // 11
|
||||
[' (Vs = 35V)', 'V'], // 12
|
||||
['VLoop @ 20mA (Vs = 18V)', 'V'], // 13
|
||||
[' (Vs = 35V)', 'V'], // 14
|
||||
['VLoop Peak Ripple', 'mV'], // 15
|
||||
['High Excitation Current', 'uA'], // 16
|
||||
['Low Excitation Current', 'uA'], // 17
|
||||
['Output Effective Power', 'mW'], // 18
|
||||
['Supply Sensitivity', '%/%Vs'], // 19
|
||||
['Open Sensor Response', 'V'], // 20
|
||||
['Lead Resistance Effect', 'C/ohm'], // 21
|
||||
['CJC Gain', 'uV/C'], // 22
|
||||
['100kHz Output Noise', 'uVrms'], // 23
|
||||
['Attenuation', 'dB'], // 24
|
||||
['150ms Step Response', 'V'], // 25
|
||||
['Output Noise', 'mVpk'], // 26
|
||||
['Over-Range', 'V'], // 27
|
||||
['Under-Range', 'V'], // 28
|
||||
['Open Loop Detect', 'mA'], // 29
|
||||
['Error @ Max Rload', '%'], // 30
|
||||
['Pass-Through Error', '%'], // 31
|
||||
],
|
||||
DSCT: [
|
||||
['Under-range Limit', 'mA'],
|
||||
['Over-range Limit', 'mA'],
|
||||
['Error @ Vloop = 10.8V', '%'],
|
||||
['Error @ Vloop = 60V', '%'],
|
||||
['Minus f.s. Exc. Current', 'uA'],
|
||||
['Plus f.s. Exc. Current', 'uA'],
|
||||
['Current Source Matching', '%'],
|
||||
['Linearity / Conformity', '%'],
|
||||
['Accuracy', '%'],
|
||||
['Lead Resistance Effects', 'C/ohm'],
|
||||
['Loop Voltage Sensitivity', '%/V'],
|
||||
['Input Resistance', 'Mohm'],
|
||||
['Open Thermocouple Response', 'mA'],
|
||||
['Frequency Response', 'dB'],
|
||||
['Step Response', '%'],
|
||||
['Output Noise', 'uArms'],
|
||||
],
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Sensor type number mapping (for input column headers)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
function getSensorNum(sentype) {
|
||||
if (!sentype) return 1;
|
||||
const s = sentype.toUpperCase().trim();
|
||||
if (s === 'V' || s === 'MV') return 1;
|
||||
if (s === 'MA') return 2;
|
||||
if (s.includes('JTC') || s === 'J') return 3;
|
||||
if (s.includes('KTC') || s === 'K') return 4;
|
||||
if (s.includes('TTC') || s === 'T') return 5;
|
||||
if (s.includes('ETC') || s === 'E' || s.includes('RTC') || s.includes('STC') || s.includes('NTC') || s.includes('BTC')) return 6;
|
||||
if (s.includes('RTD')) return 7;
|
||||
if (s === 'FBRIDGE' || s === 'HBRIDGE') return 8;
|
||||
if (s === '2WTX') return 9;
|
||||
return 1; // default voltage
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Parse raw_data from DB record
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
function parseRawData(rawData, family) {
|
||||
if (!rawData) return null;
|
||||
|
||||
const lines = rawData.split('\n').map(l => l.trim()).filter(l => l.length > 0);
|
||||
if (lines.length < 8) return null;
|
||||
|
||||
const result = {
|
||||
modelLine: '',
|
||||
accuracy: [], // 5 points: { stim, calc, meas, error, status }
|
||||
stepResponse: 0,
|
||||
statusEntries: [],
|
||||
};
|
||||
|
||||
let lineIdx = 0;
|
||||
|
||||
// Line 0: model name (quoted)
|
||||
result.modelLine = lines[lineIdx++].replace(/"/g, '').trim();
|
||||
|
||||
// Lines 1-5: accuracy points
|
||||
for (let i = 0; i < 5 && lineIdx < lines.length; i++) {
|
||||
const parts = parseCSVLine(lines[lineIdx++]);
|
||||
if (parts.length >= 5) {
|
||||
result.accuracy.push({
|
||||
stim: parseFloat(parts[0]),
|
||||
calc: parseFloat(parts[1]),
|
||||
meas: parseFloat(parts[2]),
|
||||
error: parseFloat(parts[3]),
|
||||
status: parts[4].replace(/"/g, '').trim(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Next line: step response / placeholders
|
||||
if (lineIdx < lines.length) {
|
||||
const parts = parseCSVLine(lines[lineIdx++]);
|
||||
// SCM5B/8B: "0","0",value DSCT: just value
|
||||
const lastVal = parts[parts.length - 1];
|
||||
result.stepResponse = parseFloat(lastVal) || 0;
|
||||
}
|
||||
|
||||
// Remaining lines: STATUS groups
|
||||
// SCM5B/8B: groups of 5, DSCT: groups of 4
|
||||
const groupSize = (family === 'DSCT') ? 4 : 5;
|
||||
while (lineIdx < lines.length) {
|
||||
const line = lines[lineIdx];
|
||||
// Stop if we hit the serial/date line
|
||||
if (line.match(/^"\d+-\d+[A-Za-z]?","/)) break;
|
||||
const parts = parseCSVLine(line);
|
||||
for (const p of parts) {
|
||||
result.statusEntries.push(p.replace(/"/g, ''));
|
||||
}
|
||||
lineIdx++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Simple CSV parser that handles quoted strings
|
||||
function parseCSVLine(line) {
|
||||
const parts = [];
|
||||
let current = '';
|
||||
let inQuotes = false;
|
||||
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
const ch = line[i];
|
||||
if (ch === '"') {
|
||||
inQuotes = !inQuotes;
|
||||
} else if (ch === ',' && !inQuotes) {
|
||||
parts.push(current.trim());
|
||||
current = '';
|
||||
} else {
|
||||
current += ch;
|
||||
}
|
||||
}
|
||||
parts.push(current.trim());
|
||||
return parts;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Format measured value from STATUS entry
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Format a number matching QuickBASIC STR$() behavior:
|
||||
* - Positive numbers get a leading space
|
||||
* - Leading zeros before decimal are dropped (0.03 -> .03)
|
||||
* - Rounds to 6 significant digits to clean IEEE 754 artifacts
|
||||
*/
|
||||
function r(val, fixedDecimals) {
|
||||
if (val == null || isNaN(val)) return '0';
|
||||
const rounded = parseFloat(val.toPrecision(6));
|
||||
let str;
|
||||
if (fixedDecimals != null) {
|
||||
str = rounded.toFixed(fixedDecimals);
|
||||
} else {
|
||||
str = String(rounded);
|
||||
}
|
||||
// QB STR$() drops leading zero: "0.03" -> ".03"
|
||||
str = str.replace(/^0\./, '.').replace(/^-0\./, '-.');
|
||||
// QB STR$() prepends space for positive numbers
|
||||
if (rounded >= 0 && !str.startsWith(' ')) {
|
||||
str = ' ' + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse STATUS$ entry and format measured value matching QB PRINT USING.
|
||||
* QB format strings all produce exactly 6 characters for the number:
|
||||
* "0" -> "###### &" (integer, 6 digits)
|
||||
* "1" -> "####.# &" (1 decimal, 6 chars)
|
||||
* "2" -> "####.# &" (same as 1)
|
||||
* "3" -> "##.### &" (3 decimals, 6 chars)
|
||||
* "4" -> "#.#### &" (4 decimals, 6 chars)
|
||||
*/
|
||||
function formatMeasured(statusStr) {
|
||||
if (!statusStr || statusStr.length <= 4) return null;
|
||||
|
||||
const passFail = statusStr.substring(0, 4); // "PASS" or "FAIL"
|
||||
const decimalDigit = statusStr[statusStr.length - 1];
|
||||
const valueStr = statusStr.substring(5, statusStr.length - 1).trim();
|
||||
const value = parseFloat(valueStr);
|
||||
|
||||
if (isNaN(value)) return { passFail, formatted: valueStr, width: 6 };
|
||||
|
||||
// QB PRINT USING: right-justified in 6 character positions
|
||||
// Negative sign takes one digit position
|
||||
let formatted;
|
||||
switch (decimalDigit) {
|
||||
case '0': formatted = Math.round(value).toString().padStart(6); break;
|
||||
case '1': formatted = value.toFixed(1).padStart(6); break;
|
||||
case '2': formatted = value.toFixed(1).padStart(6); break;
|
||||
case '3': formatted = value.toFixed(3).padStart(6); break;
|
||||
case '4': formatted = value.toFixed(4).padStart(6); break;
|
||||
default: formatted = value.toFixed(1).padStart(6); break;
|
||||
}
|
||||
|
||||
return { passFail, formatted, value };
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Format TSPEC display string from spec values
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
function buildTSpecs(specs, family, stepResponse) {
|
||||
if (!specs) return [];
|
||||
const tspecs = [];
|
||||
|
||||
if (family === 'SCM5B' || family === '8B') {
|
||||
tspecs[1] = ' < ' + r(specs.ISMAXNEXCL);
|
||||
tspecs[2] = ' < ' + r(specs.ISMAXFEXCL);
|
||||
tspecs[3] = ' ' + r(specs.IEXC);
|
||||
tspecs[4] = ' ' + r(specs.IEXC);
|
||||
const imatchtol = (specs.IMATCHTOL || 0) / 100;
|
||||
tspecs[5] = '+/-' + r(specs.IEXC * imatchtol, 0);
|
||||
tspecs[6] = family === '8B' ? ' < 50' : ' < ' + r(specs.OUTRES || 55);
|
||||
tspecs[7] = ''; // CJC gain - computed from polynomial, skip for now
|
||||
if (specs.VEXC) {
|
||||
const vexcAcc = Math.round(specs.VEXCACC / 100 * specs.VEXC * 1000) / 1000;
|
||||
tspecs[8] = r(specs.VEXC, 1) + '+/-' + r(vexcAcc, 3);
|
||||
} else {
|
||||
tspecs[8] = '';
|
||||
}
|
||||
tspecs[9] = '+/-' + r(specs.EXCLOADREG);
|
||||
const acc125 = Math.round((specs.ACCURACY * 1.25) * 100) / 100;
|
||||
tspecs[10] = '+/-' + r(acc125);
|
||||
tspecs[11] = ' < ' + r(specs.EXCIMAX);
|
||||
tspecs[12] = '+/-' + r(specs.LINEAR);
|
||||
tspecs[13] = '+/-' + r(specs.ACCURACY);
|
||||
tspecs[14] = '+/-' + r(stepResponse || 0, 1);
|
||||
tspecs[15] = '+/-' + r(specs.PSS || 0);
|
||||
tspecs[16] = ' >=' + r(specs.INPUTRES);
|
||||
if (specs.VOPENINMIN != null && specs.VOPENINMAX != null) {
|
||||
tspecs[17] = r(specs.VOPENINMIN, 2) + ' to ' + r(specs.VOPENINMAX, 2);
|
||||
} else {
|
||||
tspecs[17] = '';
|
||||
}
|
||||
tspecs[18] = r(specs.ATTEN) + '+/-' + r(specs.ATTENTOL);
|
||||
tspecs[19] = r(specs.STEPRMIN || 0) + ' to ' + r(specs.STEPRMAX || 0);
|
||||
tspecs[20] = ' < ' + r(specs.OUTNOISE);
|
||||
tspecs[21] = tspecs[17]; // duplicate
|
||||
} else if (family === 'DSCA') {
|
||||
tspecs[1] = ' < ' + r(specs.ISMAXNL || 0);
|
||||
tspecs[2] = ' < ' + r(specs.ISMAXFL || 0);
|
||||
tspecs[3] = '+/-' + r(specs.LINEAR1 || 0);
|
||||
tspecs[4] = '+/-' + r(specs.ACCURACY1 || 0);
|
||||
tspecs[5] = '+/-' + r(specs.LINEAR2 || 0);
|
||||
tspecs[6] = '+/-' + r(specs.ACCURACY2 || 0);
|
||||
tspecs[7] = '+/-' + r(specs.LINEAR3 || 0);
|
||||
tspecs[8] = '+/-' + r(specs.ACCURACY3 || 0);
|
||||
tspecs[9] = ' < ' + r(specs.ILIMIT || 0);
|
||||
tspecs[10] = ' > ' + r(-(specs.ILIMIT || 0));
|
||||
tspecs[11] = ' > ' + r(specs.PERCOVER || 0);
|
||||
tspecs[12] = '+/-' + r(specs.PSS || 0);
|
||||
tspecs[13] = ' >=' + r(specs.INPUTRES || 0);
|
||||
tspecs[14] = r(specs.ATTEN || 0) + '+/-' + r(specs.ATTENTOL || 0);
|
||||
tspecs[15] = r(specs.STEPRMIN || 0) + ' to ' + r(specs.STEPRMAX || 0);
|
||||
tspecs[16] = ' <=' + r(specs.OUTNOISE || 0);
|
||||
tspecs[17] = '+/-' + r(specs.COMPLIANCE || 0);
|
||||
tspecs[18] = '+/-' + r((specs.ACCURACY1 || 0) * 2);
|
||||
} else if (family === 'DSCT') {
|
||||
tspecs[1] = ''; // computed at runtime
|
||||
tspecs[2] = ''; // computed at runtime
|
||||
tspecs[3] = ' < 1';
|
||||
tspecs[4] = ' < 1';
|
||||
const iexcmTol = specs.MODNAME && specs.MODNAME.startsWith('DSCT') ? 0.05 : 0.02;
|
||||
tspecs[5] = Math.round(specs.IEXCMFS || 0) + '+/-' + Math.round((specs.IEXCMFS || 0) * iexcmTol);
|
||||
tspecs[6] = Math.round(specs.IEXCPFS || 0) + '+/-' + Math.round((specs.IEXCPFS || 0) * iexcmTol);
|
||||
tspecs[7] = '+/-' + r(specs.IMATCHTOL || 0);
|
||||
tspecs[8] = '+/- ' + r(specs.LINEAR || 0);
|
||||
tspecs[9] = '+/- ' + r(specs.ACCURACY || 0);
|
||||
tspecs[10] = '+/-' + r(stepResponse || 0, 1);
|
||||
tspecs[11] = '+/-' + r(specs.VSEN || 0);
|
||||
tspecs[12] = ' >=' + r(specs.INPUTRES || 0);
|
||||
const iopentc = specs.IOPENTC || 0;
|
||||
const maxout = specs.MAXOUT || 20;
|
||||
tspecs[13] = (iopentc > maxout ? ' > ' : ' < ') + r(iopentc);
|
||||
tspecs[14] = r(specs.ATTEN || 0) + '+/-' + r(specs.ATTENTOL || 0);
|
||||
tspecs[15] = r(specs.STEPRMIN || 0) + ' to ' + r(specs.STEPRMAX || 0);
|
||||
tspecs[16] = ' < ' + r(specs.OUTNOISE || 0);
|
||||
} else if (family === 'SCM7B') {
|
||||
const orange = (specs.MAXOUT || 5) - (specs.MINOUT || 0);
|
||||
tspecs[1] = '< ' + r(specs.ISMAXNEXCL + 6);
|
||||
tspecs[2] = '< ' + r(specs.ISMAXFEXCL + 6);
|
||||
tspecs[3] = '+/-' + r(specs.IBIAS || 0);
|
||||
tspecs[4] = ' > ' + r(specs.INPUTRES || 0);
|
||||
const calTol = 20 * orange * (specs.CALTOL || 0);
|
||||
tspecs[5] = '+/-' + r(calTol);
|
||||
tspecs[6] = '+/-' + r(calTol);
|
||||
tspecs[7] = '+/-' + r(specs.LINEAR || 0);
|
||||
tspecs[8] = '+/-' + r(specs.ACCURACY || 0);
|
||||
if (specs.VEXC) {
|
||||
const vexc5 = specs.VEXC * 0.05;
|
||||
tspecs[9] = r(specs.VEXC) + ' +/-' + r(vexc5);
|
||||
tspecs[10] = tspecs[9];
|
||||
}
|
||||
if (specs.VEXCLO) {
|
||||
const vlo5 = specs.VEXCLO * 0.05;
|
||||
tspecs[11] = r(specs.VEXCLO) + ' +/-' + r(vlo5);
|
||||
tspecs[12] = tspecs[11];
|
||||
}
|
||||
if (specs.VEXCHI) {
|
||||
const vhi5 = specs.VEXCHI * 0.05;
|
||||
tspecs[13] = r(specs.VEXCHI) + ' +/-' + r(vhi5);
|
||||
tspecs[14] = tspecs[13];
|
||||
}
|
||||
tspecs[15] = ' < 50';
|
||||
tspecs[16] = ' < ' + r(specs.EXCIMAX || 0);
|
||||
tspecs[17] = ' > ' + r(specs.EXCIMIN || 0);
|
||||
tspecs[18] = ' > ' + r(specs.PE || 0);
|
||||
tspecs[19] = '+/-' + r(specs.PSS || 0);
|
||||
tspecs[20] = ''; // Open TC - needs runtime calc
|
||||
tspecs[21] = '+/-' + r(specs.LEADRERR || 0);
|
||||
tspecs[22] = ''; // CJC - needs seebeck polynomial
|
||||
tspecs[23] = ' < ' + r(specs.OUTNOISERMS || 0);
|
||||
tspecs[24] = r(specs.ATTEN || 0) + '+/-' + r(specs.ATTENTOL || 0);
|
||||
// Step response
|
||||
if (specs.STEPRESP && specs.STEPTOL) {
|
||||
const lowV = specs.STEPRESP - specs.STEPTOL;
|
||||
const highV = specs.STEPRESP + specs.STEPTOL;
|
||||
tspecs[25] = r(lowV) + ' to ' + r(highV);
|
||||
} else {
|
||||
tspecs[25] = '';
|
||||
}
|
||||
tspecs[26] = ' < ' + r(specs.OUTNOISEVPK || 0);
|
||||
tspecs[27] = '+5 to +5.8';
|
||||
tspecs[28] = '-.9 to +1';
|
||||
tspecs[29] = '0';
|
||||
tspecs[30] = ''; // Compliance - needs runtime calc
|
||||
tspecs[31] = '+/-' + r(specs.ACCURACY || 0);
|
||||
}
|
||||
|
||||
return tspecs;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Format accuracy value based on sensor type
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
function formatAccuracyLine(point, sensorNum, maxIn) {
|
||||
let stimStr;
|
||||
if (sensorNum >= 3 && sensorNum <= 6) {
|
||||
// Temperature: +####.##
|
||||
stimStr = formatSigned(point.stim, 2, 8);
|
||||
} else if (sensorNum === 7) {
|
||||
// Resistance: #####.##
|
||||
stimStr = point.stim.toFixed(2).padStart(8);
|
||||
} else {
|
||||
// Voltage/Current: +###.###
|
||||
const scale = (maxIn != null && maxIn < 1) ? 1000 : 1;
|
||||
stimStr = formatSigned(point.stim * scale, 3, 8);
|
||||
}
|
||||
|
||||
const calcStr = formatSigned(point.calc, 3, 7);
|
||||
const measStr = formatSigned(point.meas, 3, 7);
|
||||
const errorStr = formatSigned(point.error, 3, 8);
|
||||
|
||||
return ' ' + stimStr + ' ' + calcStr + ' ' + measStr + ' ' + errorStr + ' ' + point.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text at a specific column position (0-indexed) in a string.
|
||||
* Pads with spaces if the string is shorter than the target column.
|
||||
*/
|
||||
function setCol(str, col, text) {
|
||||
while (str.length < col) str += ' ';
|
||||
return str + text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pad string to reach a column position (for inline TAB simulation).
|
||||
* Returns spaces needed to reach the column from current position.
|
||||
*/
|
||||
function padToCol(str, col) {
|
||||
const needed = col - str.length;
|
||||
return needed > 0 ? ' '.repeat(needed) : ' ';
|
||||
}
|
||||
|
||||
function formatSigned(val, decimals, width) {
|
||||
const sign = val >= 0 ? '+' : '';
|
||||
const str = sign + val.toFixed(decimals);
|
||||
return str.padStart(width);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Main: generate exact-match TXT datasheet
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Generate an exact-match TXT datasheet from a DB record and model specs.
|
||||
* @param {object} record - DB record with raw_data, model_number, serial_number, test_date
|
||||
* @param {object} specs - Model spec record from spec-reader
|
||||
* @returns {string|null} Formatted TXT datasheet, or null if data is insufficient
|
||||
*/
|
||||
function generateExactDatasheet(record, specs) {
|
||||
const family = getFamily(record.model_number);
|
||||
if (!family) return null;
|
||||
|
||||
const parsed = (family === 'SCM7B')
|
||||
? parse7BRawData(record.raw_data)
|
||||
: parseRawData(record.raw_data, family);
|
||||
if (!parsed) return null;
|
||||
if (family !== 'SCM7B' && parsed.accuracy.length < 5) return null;
|
||||
|
||||
const dataLines = DATA_LINES[family];
|
||||
if (!dataLines) return null;
|
||||
|
||||
const sentype = specs ? specs.SENTYPE : '';
|
||||
const sensorNum = getSensorNum(sentype);
|
||||
const maxIn = specs ? specs.MAXIN : 10;
|
||||
const tspecs = specs ? buildTSpecs(specs, family, parsed.stepResponse) : [];
|
||||
|
||||
// Format test date from YYYY-MM-DD to MM-DD-YYYY
|
||||
const dateParts = (record.test_date || '').split('-');
|
||||
const dateStr = dateParts.length === 3
|
||||
? `${dateParts[1]}-${dateParts[2]}-${dateParts[0]}`
|
||||
: record.test_date || '';
|
||||
|
||||
let modelName = specs ? specs.MODNAME : record.model_number;
|
||||
// 7B header prepends "SCM" to the model name
|
||||
if (family === 'SCM7B' && !modelName.toUpperCase().startsWith('SCM')) {
|
||||
modelName = 'SCM' + modelName;
|
||||
}
|
||||
|
||||
const lines = [];
|
||||
const TAB5 = ' '; // 4 spaces = TAB(5) in QB (0-indexed)
|
||||
|
||||
// ---- Header ----
|
||||
lines.push(TAB5 + 'DATAFORTH CORPORATION Phone: (520) 741-1404');
|
||||
lines.push(TAB5 + '3331 E. Hemisphere Loop Fax: (520) 741-0762');
|
||||
lines.push(TAB5 + 'Tucson, AZ 85706 USA email: info@dataforth.com');
|
||||
lines.push('');
|
||||
lines.push(' TEST DATA SHEET');
|
||||
lines.push(TAB5 + '~'.repeat(71));
|
||||
// QB: PRINT #9, TAB(5); "Date: "; DATE$
|
||||
// PRINT #9, TAB(5); "Model: "; SPECS.MODNAME
|
||||
// PRINT #9, TAB(5); "SN: "; TAB(12); SN$
|
||||
lines.push(TAB5 + 'Date: ' + dateStr);
|
||||
lines.push(TAB5 + 'Model: ' + modelName);
|
||||
let snLine = TAB5 + 'SN: ';
|
||||
snLine = setCol(snLine, 11, record.serial_number); // TAB(12) = index 11
|
||||
lines.push(snLine);
|
||||
lines.push('');
|
||||
|
||||
// ---- Accuracy Test ----
|
||||
// 7B CSV format doesn't include individual accuracy test points (only error pcts in LOGIT)
|
||||
// The accuracy data is only in the SHT files, not the DAT files
|
||||
if (family === 'SCM7B') {
|
||||
// Skip accuracy section entirely for 7B — data not available from DAT format
|
||||
} else {
|
||||
lines.push(' ACCURACY TEST');
|
||||
lines.push('');
|
||||
lines.push(' Calculated Measured');
|
||||
|
||||
// Input column header based on sensor type
|
||||
let inputHeader;
|
||||
if (sensorNum >= 3 && sensorNum <= 6) {
|
||||
inputHeader = ' Temp. (C)';
|
||||
} else if (sensorNum === 2 || sensorNum === 9) {
|
||||
inputHeader = ' Iin (mA)';
|
||||
} else if (sensorNum === 7) {
|
||||
inputHeader = ' Rin (ohms)';
|
||||
} else {
|
||||
inputHeader = (maxIn != null && maxIn < 1) ? ' Vin (mV)' : ' Vin (V)';
|
||||
}
|
||||
lines.push(' ' + inputHeader + ' Vout (V) Vout (V)* Error (%) Status');
|
||||
lines.push(TAB5 + '========== ========== ========== ========= ========');
|
||||
|
||||
for (const point of parsed.accuracy) {
|
||||
lines.push(formatAccuracyLine(point, sensorNum, maxIn));
|
||||
}
|
||||
lines.push('');
|
||||
} // end accuracy section conditional
|
||||
|
||||
// ---- Final Test Results ----
|
||||
// QB column positions (1-indexed): TAB(31), TAB(47), TAB(60-speclen), TAB(61), TAB(71)
|
||||
lines.push(' FINAL TEST RESULTS');
|
||||
lines.push('');
|
||||
// QB: TAB(12); "Parameter"; TAB(30); "Measured Value"; TAB(51); "Specification "; TAB(70); "Status"
|
||||
let hdr1 = setCol('', 11, 'Parameter');
|
||||
hdr1 = setCol(hdr1, 29, 'Measured Value');
|
||||
hdr1 = setCol(hdr1, 50, 'Specification ');
|
||||
hdr1 = setCol(hdr1, 69, 'Status');
|
||||
lines.push(hdr1);
|
||||
// QB: TAB(5); "======================="; TAB(30); "==============="; TAB(47); "====================="; TAB(70); "======"
|
||||
let hdr2 = setCol('', 4, '=======================');
|
||||
hdr2 = setCol(hdr2, 29, '===============');
|
||||
hdr2 = setCol(hdr2, 46, '=====================');
|
||||
hdr2 = setCol(hdr2, 69, '======');
|
||||
lines.push(hdr2);
|
||||
|
||||
for (let i = 0; i < dataLines.length && i < parsed.statusEntries.length; i++) {
|
||||
const status = parsed.statusEntries[i];
|
||||
if (!status || status.length <= 4) continue; // Skip if no measured data
|
||||
|
||||
const [paramName, paramUnit] = dataLines[i];
|
||||
let unit = paramUnit;
|
||||
|
||||
// Unit overrides per QB logic
|
||||
if (family === 'SCM5B' || family === '8B') {
|
||||
if (i === 13 && sensorNum === 7) unit = 'ohm/ohm';
|
||||
if (i === 14 && (sensorNum === 5 || sensorNum === 6)) unit = 'C/V';
|
||||
}
|
||||
|
||||
const measured = formatMeasured(status);
|
||||
if (!measured) continue;
|
||||
|
||||
// Build line matching QB TAB positions (converting to 0-indexed for string ops)
|
||||
// TAB(5): parameter name
|
||||
// TAB(31): measured value (6 chars right-justified) + space + unit
|
||||
// TAB(60-speclen): spec string right-aligned to end at col 60
|
||||
// TAB(61): unit
|
||||
// TAB(71): PASS/FAIL
|
||||
let line = '';
|
||||
line = setCol(line, 4, paramName); // TAB(5) = index 4
|
||||
line = setCol(line, 30, measured.formatted + ' ' + unit); // TAB(31) = index 30
|
||||
|
||||
const tspec = tspecs[i + 1]; // 1-indexed in TSPECS
|
||||
if (tspec) {
|
||||
const specLen = tspec.length;
|
||||
line = setCol(line, 59 - specLen, tspec); // TAB(60-speclen)
|
||||
line = setCol(line, 60, unit); // TAB(61) = index 60
|
||||
}
|
||||
line = setCol(line, 70, measured.passFail); // TAB(71) = index 70
|
||||
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
// ---- Footer ----
|
||||
// 240 VAC / Hi-Pot (conditional by family/model)
|
||||
if (family === 'SCM5B') {
|
||||
const mn = (modelName || '').trim();
|
||||
if (!mn.startsWith('SCM5BPT') && !mn.startsWith('SCM5B-1369')) {
|
||||
lines.push(TAB5 + '240 VAC Withstand' + ''.padEnd(49) + 'PASS');
|
||||
lines.push(TAB5 + 'Hi-Pot' + ''.padEnd(60) + 'PASS');
|
||||
}
|
||||
} else if (family === '8B') {
|
||||
const mn = (modelName || '').trim();
|
||||
if (!mn.startsWith('8BPT')) {
|
||||
lines.push(TAB5 + 'VAC Withstand' + ''.padEnd(53) + 'PASS');
|
||||
lines.push(TAB5 + 'Hi-Pot' + ''.padEnd(60) + 'PASS');
|
||||
}
|
||||
} else if (family === 'SCM7B') {
|
||||
const mn = (modelName || '').toUpperCase();
|
||||
if (!mn.includes('7BPT')) {
|
||||
let vac = setCol(TAB5 + '120VAC Withstand', 70, 'PASS');
|
||||
lines.push(vac);
|
||||
let hp = setCol(TAB5 + 'Hi-Pot', 70, 'PASS');
|
||||
lines.push(hp);
|
||||
}
|
||||
} else if (family === 'DSCA') {
|
||||
lines.push(TAB5 + '240VAC Withstand' + ''.padEnd(50) + 'PASS');
|
||||
lines.push(TAB5 + 'Hi-Pot' + ''.padEnd(60) + 'PASS');
|
||||
} else if (family === 'DSCT') {
|
||||
const mn = (modelName || '').toUpperCase();
|
||||
if (!mn.startsWith('SCMHVAS')) {
|
||||
lines.push(TAB5 + '240 VAC Withstand' + ''.padEnd(49) + 'PASS');
|
||||
lines.push(TAB5 + 'Hi-Pot' + ''.padEnd(60) + 'PASS');
|
||||
}
|
||||
}
|
||||
|
||||
// Underline + Check List
|
||||
lines.push(TAB5 + '_'.repeat(71));
|
||||
if (family === 'SCM7B') {
|
||||
lines.push(' Packing Check List');
|
||||
lines.push('');
|
||||
lines.push(setCol(TAB5 + 'Module Appearance: _____', 44, 'Mounting Screw: _____'));
|
||||
lines.push('');
|
||||
lines.push(setCol(TAB5 + 'Pins Straight: _____', 44, 'Module Header: _____'));
|
||||
lines.push('');
|
||||
lines.push(setCol(TAB5 + 'Tested by: _____________', 44, 'QC: _______________'));
|
||||
} else if (family !== 'DSCA') {
|
||||
lines.push(' Check List');
|
||||
lines.push('');
|
||||
lines.push(setCol(TAB5 + 'Module Appearance: __X__', 44, 'Mounting Screw: __X__'));
|
||||
lines.push('');
|
||||
lines.push(setCol(TAB5 + 'Pins Straight: __X__', 44, 'Module Header: __X__'));
|
||||
}
|
||||
|
||||
// DSCA current output load note
|
||||
if (family === 'DSCA' && specs && specs.OUTSIGTYPE && specs.OUTSIGTYPE.trim().toUpperCase() === 'CURRENT') {
|
||||
lines.push(TAB5 + 'Standard output load for test is 250 ohms.');
|
||||
}
|
||||
|
||||
lines.push('');
|
||||
lines.push(TAB5 + 'It is hereby certified that the above product is in conformance with');
|
||||
lines.push(TAB5 + 'all requirements to the extent specified. This product is not');
|
||||
lines.push(TAB5 + 'authorized or warranted for use in life support devices and/or systems.');
|
||||
lines.push('');
|
||||
lines.push(TAB5 + '* NIST traceable calibration certificates support Measured Value data.');
|
||||
lines.push(TAB5 + ' Calibration services are available through ANSI/NCSL Z540-1 and');
|
||||
lines.push(TAB5 + ' ISO Guide 25 Certified Metrology Labs.');
|
||||
lines.push('');
|
||||
|
||||
return lines.join('\r\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse 7B raw_data (single CSV line format)
|
||||
* Format: STAGE: MODEL,SN,DATE,VERSION,DMMSERIAL,val1,...val31,err1,...errN
|
||||
* val=9999 means not tested, [val] means FAIL
|
||||
*/
|
||||
function parse7BRawData(rawData) {
|
||||
if (!rawData) return null;
|
||||
|
||||
const match = rawData.match(/^([A-Z-]+):\s*(.*)$/);
|
||||
if (!match) return null;
|
||||
|
||||
const parts = match[2].split(',');
|
||||
if (parts.length < 36) return null; // model + sn + date + version + dmmserial + 31 values minimum
|
||||
|
||||
const result = {
|
||||
modelLine: parts[0].trim(),
|
||||
accuracy: [],
|
||||
stepResponse: 0,
|
||||
statusEntries: [],
|
||||
};
|
||||
|
||||
// Values start at index 5 (after model, sn, date, version, dmmserial)
|
||||
for (let i = 0; i < 31; i++) {
|
||||
const rawVal = (parts[5 + i] || '').trim();
|
||||
|
||||
if (rawVal === '9999' || rawVal === '') {
|
||||
// Not tested - push short "PASS" (will be skipped by formatter)
|
||||
result.statusEntries.push('PASS');
|
||||
} else if (rawVal.startsWith('[')) {
|
||||
// FAIL - bracketed value
|
||||
const val = rawVal.replace(/[\[\]]/g, '').trim();
|
||||
const numVal = parseFloat(val);
|
||||
if (isNaN(numVal) || numVal === 0) {
|
||||
result.statusEntries.push('FAIL');
|
||||
} else {
|
||||
const decimals = guessDecimals(numVal);
|
||||
result.statusEntries.push('FAIL ' + val + decimals);
|
||||
}
|
||||
} else {
|
||||
// PASS with value
|
||||
const numVal = parseFloat(rawVal);
|
||||
if (isNaN(numVal)) {
|
||||
result.statusEntries.push('PASS');
|
||||
} else {
|
||||
const decimals = guessDecimals(numVal);
|
||||
result.statusEntries.push('PASS ' + rawVal.trim() + decimals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error percentages follow the 31 values - these are the accuracy test point errors
|
||||
const errorStart = 5 + 31;
|
||||
for (let i = errorStart; i < parts.length; i++) {
|
||||
const val = parseFloat((parts[i] || '').trim());
|
||||
if (!isNaN(val)) {
|
||||
result.accuracy.push({
|
||||
stim: 0, // Stimulus not stored in 7B CSV format
|
||||
calc: 0,
|
||||
meas: 0,
|
||||
error: val * 100, // Convert fraction to percentage
|
||||
status: 'PASS',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess the decimal format digit based on value magnitude
|
||||
*/
|
||||
function guessDecimals(val) {
|
||||
const abs = Math.abs(val);
|
||||
if (abs === 0) return '0';
|
||||
if (abs >= 100) return '0';
|
||||
if (abs >= 10) return '1';
|
||||
if (abs >= 1) return '1';
|
||||
if (abs >= 0.1) return '3';
|
||||
return '4';
|
||||
}
|
||||
|
||||
module.exports = { generateExactDatasheet, parseRawData, parse7BRawData, DATA_LINES };
|
||||
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* Datasheet Generator
|
||||
* Generates formatted test data sheets from database records
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generate a datasheet from a test record
|
||||
* @param {Object} record - Database record
|
||||
* @param {string} format - Output format ('html' or 'txt')
|
||||
* @returns {string} Formatted datasheet
|
||||
*/
|
||||
function generateDatasheet(record, format = 'html') {
|
||||
if (format === 'html') {
|
||||
return generateHtmlDatasheet(record);
|
||||
} else {
|
||||
return generateTextDatasheet(record);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HTML datasheet
|
||||
*/
|
||||
function generateHtmlDatasheet(record) {
|
||||
const testDate = formatDate(record.test_date);
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Data Sheet - ${record.serial_number}</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.company {
|
||||
font-weight: bold;
|
||||
}
|
||||
.contact {
|
||||
text-align: right;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
border-top: 2px solid #333;
|
||||
border-bottom: 2px solid #333;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.info {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.info-row {
|
||||
display: flex;
|
||||
}
|
||||
.info-label {
|
||||
width: 100px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.raw-data {
|
||||
background: #f5f5f5;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
white-space: pre-wrap;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 11px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.result {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.result.pass { background: #d4edda; color: #155724; }
|
||||
.result.fail { background: #f8d7da; color: #721c24; }
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
border-top: 1px solid #333;
|
||||
padding-top: 20px;
|
||||
font-size: 10px;
|
||||
}
|
||||
@media print {
|
||||
body { margin: 0; }
|
||||
.no-print { display: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="company">
|
||||
DATAFORTH CORPORATION<br>
|
||||
3331 E. Hemisphere Loop<br>
|
||||
Tucson, AZ 85706 USA
|
||||
</div>
|
||||
<div class="contact">
|
||||
Phone: (520) 741-1404<br>
|
||||
Fax: (520) 741-0762<br>
|
||||
Email: info@dataforth.com
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>TEST DATA SHEET</h1>
|
||||
|
||||
<div class="info">
|
||||
<div class="info-row"><span class="info-label">Date:</span> ${testDate}</div>
|
||||
<div class="info-row"><span class="info-label">Model:</span> ${record.model_number}</div>
|
||||
<div class="info-row"><span class="info-label">SN:</span> ${record.serial_number}</div>
|
||||
<div class="info-row"><span class="info-label">Log Type:</span> ${record.log_type}</div>
|
||||
<div class="info-row"><span class="info-label">Station:</span> ${record.test_station || 'N/A'}</div>
|
||||
</div>
|
||||
|
||||
<div class="result ${record.overall_result?.toLowerCase() || ''}">
|
||||
OVERALL RESULT: ${record.overall_result || 'UNKNOWN'}
|
||||
</div>
|
||||
|
||||
<h3>Test Data</h3>
|
||||
<div class="raw-data">${escapeHtml(record.raw_data || 'No data available')}</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.</p>
|
||||
<p>Source: ${record.source_file}</p>
|
||||
<p>Record ID: ${record.id}</p>
|
||||
</div>
|
||||
|
||||
<div class="no-print" style="margin-top: 20px; text-align: center;">
|
||||
<button onclick="window.print()">Print Datasheet</button>
|
||||
<button onclick="window.close()">Close</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate plain text datasheet
|
||||
*/
|
||||
function generateTextDatasheet(record) {
|
||||
const testDate = formatDate(record.test_date);
|
||||
const line = '='.repeat(75);
|
||||
const tilde = '~'.repeat(75);
|
||||
|
||||
return `DATAFORTH CORPORATION Phone: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
TEST DATA SHEET
|
||||
${tilde}
|
||||
Date: ${testDate}
|
||||
Model: ${record.model_number}
|
||||
SN: ${record.serial_number}
|
||||
Log Type: ${record.log_type}
|
||||
Station: ${record.test_station || 'N/A'}
|
||||
|
||||
OVERALL RESULT: ${record.overall_result || 'UNKNOWN'}
|
||||
|
||||
${line}
|
||||
TEST DATA
|
||||
${line}
|
||||
|
||||
${record.raw_data || 'No data available'}
|
||||
|
||||
${line}
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
Source: ${record.source_file}
|
||||
Record ID: ${record.id}
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format date for display
|
||||
*/
|
||||
function formatDate(dateStr) {
|
||||
if (!dateStr) return 'Unknown';
|
||||
// Convert YYYY-MM-DD to MM-DD-YYYY
|
||||
const [year, month, day] = dateStr.split('-');
|
||||
return `${month}-${day}-${year}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape HTML special characters
|
||||
*/
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
module.exports = { generateDatasheet };
|
||||
@@ -0,0 +1,81 @@
|
||||
"""Single-session SSH+SFTP batch fetcher for AD2 -> AD1 Engineering share."""
|
||||
import os, sys, time, base64, paramiko
|
||||
|
||||
import subprocess, yaml as _yaml
|
||||
|
||||
HOST = '192.168.0.6'
|
||||
USER = 'sysadmin'
|
||||
|
||||
def _pwd():
|
||||
r = subprocess.run(['sops','-d','D:/vault/clients/dataforth/ad2.sops.yaml'],
|
||||
capture_output=True, text=True, timeout=30, check=True)
|
||||
return _yaml.safe_load(r.stdout)['credentials']['password'].replace('\\','')
|
||||
|
||||
PWD = _pwd()
|
||||
|
||||
LOCAL_ROOT = r'D:\claudetools\projects\dataforth-dos\datasheet-pipeline\scmvas-hvas-research'
|
||||
REMOTE_BASE = r'\\AD1\Engineering\ENGR\ATE\High Voltage Input Module Test'
|
||||
AD2_STAGE = r'C:\Users\sysadmin\Documents\scmvas_stage'
|
||||
|
||||
def connect():
|
||||
c = paramiko.SSHClient()
|
||||
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
c.connect(HOST, username=USER, password=PWD, timeout=30, banner_timeout=30, look_for_keys=False, allow_agent=False)
|
||||
return c
|
||||
|
||||
def run(c, cmd, timeout=120):
|
||||
stdin, stdout, stderr = c.exec_command(cmd, timeout=timeout)
|
||||
out = stdout.read().decode('utf-8', errors='replace')
|
||||
err = stderr.read().decode('utf-8', errors='replace')
|
||||
rc = stdout.channel.recv_exit_status()
|
||||
return rc, out, err
|
||||
|
||||
def ps(c, command):
|
||||
enc = base64.b64encode(command.encode('utf-16-le')).decode()
|
||||
return run(c, f'powershell -NoProfile -EncodedCommand {enc}', timeout=300)
|
||||
|
||||
def copy_one(c, remote_src, stage_dir):
|
||||
script = f'Copy-Item -LiteralPath "{remote_src}" -Destination "{stage_dir}\\" -Force -ErrorAction Stop; Write-Host "OK"'
|
||||
rc, out, err = ps(c, script)
|
||||
status = 'OK' if 'OK' in out and rc == 0 else f'FAIL: {err.strip() or out.strip()}'
|
||||
print(f'[{status}] {os.path.basename(remote_src)}')
|
||||
|
||||
def main():
|
||||
c = connect()
|
||||
try:
|
||||
ps(c, f'New-Item -ItemType Directory -Force -Path "{AD2_STAGE}" | Out-Null')
|
||||
|
||||
files = [
|
||||
f'{REMOTE_BASE}\\TESTHV3.BAS',
|
||||
f'{REMOTE_BASE}\\LIBATE3.BAS',
|
||||
f'{REMOTE_BASE}\\DBHV.BAS',
|
||||
f'{REMOTE_BASE}\\Readme.txt',
|
||||
f'{REMOTE_BASE}\\HVDATA\\hvin.dat',
|
||||
f'{REMOTE_BASE}\\HVDATA\\hvsort.dat',
|
||||
f'{REMOTE_BASE}\\Released\\TESTHV3.BAS',
|
||||
f'{REMOTE_BASE}\\Released\\NLIBATE3.BAS',
|
||||
f'{REMOTE_BASE}\\Released\\TESTHV4.BAS',
|
||||
f'{REMOTE_BASE}\\Released\\TESTHV3.MAK',
|
||||
]
|
||||
for f in files:
|
||||
copy_one(c, f, AD2_STAGE)
|
||||
|
||||
# Rename to disambiguate source locations
|
||||
rc, out, err = ps(c, f'Get-ChildItem -LiteralPath "{AD2_STAGE}" | Select-Object Name,Length | Format-Table -AutoSize | Out-String')
|
||||
print('\n=== AD2 stage ===')
|
||||
print(out)
|
||||
|
||||
# SFTP pull everything
|
||||
out_dir = os.path.join(LOCAL_ROOT, 'source')
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
sftp = c.open_sftp()
|
||||
stage_posix = AD2_STAGE.replace('\\', '/')
|
||||
for e in sftp.listdir(stage_posix):
|
||||
sftp.get(f'{stage_posix}/{e}', os.path.join(out_dir, e))
|
||||
print(f'[pulled] {e}')
|
||||
sftp.close()
|
||||
finally:
|
||||
c.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,62 @@
|
||||
"""Find and pull sample VASLOG / Engineering-Tested TXTs."""
|
||||
import paramiko, base64, os, posixpath
|
||||
|
||||
import subprocess, yaml as _yaml
|
||||
|
||||
HOST = '192.168.0.6'
|
||||
USER = 'sysadmin'
|
||||
|
||||
def _pwd():
|
||||
r = subprocess.run(['sops','-d','D:/vault/clients/dataforth/ad2.sops.yaml'],
|
||||
capture_output=True, text=True, timeout=30, check=True)
|
||||
return _yaml.safe_load(r.stdout)['credentials']['password'].replace('\\','')
|
||||
|
||||
PWD = _pwd()
|
||||
LOCAL_SAMPLES = r'D:\claudetools\projects\dataforth-dos\datasheet-pipeline\scmvas-hvas-research\samples'
|
||||
|
||||
os.makedirs(LOCAL_SAMPLES, exist_ok=True)
|
||||
|
||||
def connect():
|
||||
c = paramiko.SSHClient()
|
||||
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
c.connect(HOST, username=USER, password=PWD, timeout=30, banner_timeout=30, look_for_keys=False, allow_agent=False)
|
||||
return c
|
||||
|
||||
def ps(c, cmd, to=300):
|
||||
enc = base64.b64encode(cmd.encode('utf-16-le')).decode()
|
||||
stdin, stdout, stderr = c.exec_command(f'powershell -NoProfile -EncodedCommand {enc}', timeout=to)
|
||||
return stdout.read().decode('utf-8', errors='replace'), stderr.read().decode('utf-8', errors='replace')
|
||||
|
||||
def main():
|
||||
c = connect()
|
||||
try:
|
||||
# 1. Probe NAS share for VASLOG paths. NAS is accessed from AD2 as \\D2TESTNAS\test mounted somewhere
|
||||
print('=== Check C:\\Shares\\test (NAS mirror) for VASLOG ===')
|
||||
out, err = ps(c, r'''Get-ChildItem -LiteralPath 'C:\Shares\test' -Directory | Select-Object Name,LastWriteTime | Format-Table -AutoSize | Out-String''')
|
||||
print(out)
|
||||
|
||||
print('=== Check NAS-side LOGS/VASLOG via C:\\Shares\\test ===')
|
||||
for p in [r'C:\Shares\test\LOGS', r'C:\Shares\test\LOGS\VASLOG']:
|
||||
out, err = ps(c, f'''if (Test-Path -LiteralPath '{p}') {{ Get-ChildItem -LiteralPath '{p}' -Force | Select-Object Name,Mode,Length,LastWriteTime | Format-Table -AutoSize | Out-String }} else {{ Write-Host 'MISSING: {p}' }}''')
|
||||
print(f'--- {p} ---')
|
||||
print(out)
|
||||
|
||||
# 2. Try under TS-3R directory inside the test share if stations upload their logs
|
||||
print('=== Search for VASLOG anywhere in test share (recursive, limited) ===')
|
||||
out, err = ps(c, r'''Get-ChildItem -LiteralPath 'C:\Shares\test' -Recurse -Directory -Force -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'VASLOG|vaslog' } | Select-Object FullName | Format-List | Out-String''')
|
||||
print(out[:2000])
|
||||
|
||||
# 3. Also check NAS directly - see if we have access via UNC
|
||||
print('=== NAS UNC probe ===')
|
||||
out, err = ps(c, r'''if (Test-Path -LiteralPath '\\D2TESTNAS\test\LOGS\VASLOG') { Get-ChildItem -LiteralPath '\\D2TESTNAS\test\LOGS\VASLOG' -Force | Select-Object Name,Length,LastWriteTime | Format-Table -AutoSize | Out-String } else { Write-Host 'No direct UNC access' }''')
|
||||
print(out)
|
||||
|
||||
# 4. Look up all STAGE locations where TS stations push TXT
|
||||
print('=== TS station TXT upload points ===')
|
||||
out, err = ps(c, r'''Get-ChildItem -LiteralPath 'C:\Shares\test' -Directory -Force | ForEach-Object { $n = $_.Name; try { $c = (Get-ChildItem -LiteralPath $_.FullName -File -Filter '*.txt' -ErrorAction SilentlyContinue).Count; Write-Host "$n : $c txt files" } catch {} }''')
|
||||
print(out[:3000])
|
||||
finally:
|
||||
c.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,42 @@
|
||||
"""Focused VASLOG probe."""
|
||||
import paramiko, base64, os
|
||||
|
||||
import subprocess, yaml as _yaml
|
||||
|
||||
HOST='192.168.0.6'; USER='sysadmin'
|
||||
|
||||
def _pwd():
|
||||
r = subprocess.run(['sops','-d','D:/vault/clients/dataforth/ad2.sops.yaml'],
|
||||
capture_output=True, text=True, timeout=30, check=True)
|
||||
return _yaml.safe_load(r.stdout)['credentials']['password'].replace('\\','')
|
||||
|
||||
PWD = _pwd()
|
||||
LOCAL = r'D:\claudetools\projects\dataforth-dos\datasheet-pipeline\scmvas-hvas-research\samples'
|
||||
os.makedirs(LOCAL, exist_ok=True)
|
||||
|
||||
def ps(c, cmd, to=300):
|
||||
enc = base64.b64encode(cmd.encode('utf-16-le')).decode()
|
||||
stdin, stdout, stderr = c.exec_command(f'powershell -NoProfile -EncodedCommand {enc}', timeout=to)
|
||||
return stdout.read().decode('utf-8', errors='replace'), stderr.read().decode('utf-8', errors='replace')
|
||||
|
||||
c = paramiko.SSHClient()
|
||||
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
c.connect(HOST, username=USER, password=PWD, timeout=30, look_for_keys=False, allow_agent=False)
|
||||
|
||||
queries = [
|
||||
('TS-3R root', r'Get-ChildItem -LiteralPath "C:\Shares\test\TS-3R" -Force | Select Name,Mode,LastWriteTime | Format-Table -AutoSize | Out-String'),
|
||||
('TS-3R\\LOGS', r'if (Test-Path "C:\Shares\test\TS-3R\LOGS") { Get-ChildItem "C:\Shares\test\TS-3R\LOGS" -Force | Select Name,Mode,LastWriteTime | Format-Table -AutoSize | Out-String } else { "MISS" }'),
|
||||
('TS-3R VASLOG', r'if (Test-Path "C:\Shares\test\TS-3R\LOGS\VASLOG") { Get-ChildItem "C:\Shares\test\TS-3R\LOGS\VASLOG" -Force | Select Name,Mode,Length,LastWriteTime | Format-Table -AutoSize | Out-String } else { "MISS VASLOG" }'),
|
||||
('Corrected HVAS', r'Get-ChildItem "C:\Shares\test\Corrected HVAS Files" -Force -ErrorAction SilentlyContinue | Select Name,Mode,Length,LastWriteTime | Format-Table -AutoSize | Out-String'),
|
||||
('STAGE sample', r'Get-ChildItem "C:\Shares\test\STAGE" -Filter *.TXT -File -ErrorAction SilentlyContinue | Select -First 20 Name,Length | Format-Table -AutoSize | Out-String'),
|
||||
('Recurse VASLOG', r'Get-ChildItem "C:\Shares\test" -Recurse -Directory -Force -ErrorAction SilentlyContinue | Where-Object { $_.Name -match "VASLOG|HVAS" } | Select FullName | Format-List | Out-String'),
|
||||
]
|
||||
|
||||
try:
|
||||
for label, q in queries:
|
||||
print(f'\n=== {label} ===')
|
||||
out, err = ps(c, q)
|
||||
print(out[:3000])
|
||||
if err: print('[stderr]', err[:500])
|
||||
finally:
|
||||
c.close()
|
||||
@@ -0,0 +1,83 @@
|
||||
"""Pull samples from VASLOG, VASLOG - Engineering Tested, and Corrected HVAS Files."""
|
||||
import paramiko, base64, os
|
||||
|
||||
import subprocess, yaml as _yaml
|
||||
|
||||
HOST='192.168.0.6'; USER='sysadmin'
|
||||
|
||||
def _pwd():
|
||||
r = subprocess.run(['sops','-d','D:/vault/clients/dataforth/ad2.sops.yaml'],
|
||||
capture_output=True, text=True, timeout=30, check=True)
|
||||
return _yaml.safe_load(r.stdout)['credentials']['password'].replace('\\','')
|
||||
|
||||
PWD = _pwd()
|
||||
LOCAL = r'D:\claudetools\projects\dataforth-dos\datasheet-pipeline\scmvas-hvas-research\samples'
|
||||
os.makedirs(LOCAL, exist_ok=True)
|
||||
os.makedirs(os.path.join(LOCAL, 'vaslog-dat'), exist_ok=True)
|
||||
os.makedirs(os.path.join(LOCAL, 'vaslog-engtxt'), exist_ok=True)
|
||||
os.makedirs(os.path.join(LOCAL, 'corrected-hvas'), exist_ok=True)
|
||||
|
||||
def ps(c, cmd, to=120):
|
||||
enc = base64.b64encode(cmd.encode('utf-16-le')).decode()
|
||||
stdin, stdout, stderr = c.exec_command(f'powershell -NoProfile -EncodedCommand {enc}', timeout=to)
|
||||
return stdout.read().decode('utf-8', errors='replace')
|
||||
|
||||
c = paramiko.SSHClient()
|
||||
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
c.connect(HOST, username=USER, password=PWD, timeout=30, look_for_keys=False, allow_agent=False)
|
||||
sftp = c.open_sftp()
|
||||
|
||||
try:
|
||||
# List the Engineering-Tested TXT folder
|
||||
print('=== VASLOG - Engineering Tested listing ===')
|
||||
out = ps(c, r'Get-ChildItem "C:\Shares\test\TS-3R\LOGS\VASLOG\VASLOG - Engineering Tested" -Force | Select Name,Length,LastWriteTime | Format-Table -AutoSize | Out-String')
|
||||
print(out)
|
||||
|
||||
# Pull all .DAT files from VASLOG (they're small, total ~250KB)
|
||||
print('\n=== Pulling VASLOG .DAT files ===')
|
||||
for name in ['HVAS-M01.DAT','HVAS-M02.DAT','HVAS-M03.DAT','HVAS-M04.DAT','HVAS-MPT.DAT',
|
||||
'VAS-M100.DAT','VAS-M200.DAT','VAS-M300.DAT','VAS-M400.DAT',
|
||||
'VAS-M500.DAT','VAS-M600.DAT','VAS-M650.DAT','VAS-M700.DAT','VAS-MPT.DAT']:
|
||||
src = f'C:/Shares/test/TS-3R/LOGS/VASLOG/{name}'
|
||||
dst = os.path.join(LOCAL, 'vaslog-dat', name)
|
||||
try:
|
||||
sftp.get(src, dst)
|
||||
print(f' pulled {name}')
|
||||
except Exception as e:
|
||||
print(f' MISS {name}: {e}')
|
||||
|
||||
# Pull 5 sample Engineering Tested TXTs
|
||||
print('\n=== Pulling VASLOG Engineering Tested TXTs (first 10) ===')
|
||||
engtxt_dir_posix = 'C:/Shares/test/TS-3R/LOGS/VASLOG/VASLOG - Engineering Tested'
|
||||
try:
|
||||
entries = sftp.listdir(engtxt_dir_posix)
|
||||
print(f' {len(entries)} entries, pulling first 10')
|
||||
for name in entries[:10]:
|
||||
try:
|
||||
sftp.get(f'{engtxt_dir_posix}/{name}', os.path.join(LOCAL, 'vaslog-engtxt', name))
|
||||
print(f' pulled {name}')
|
||||
except Exception as e:
|
||||
print(f' MISS {name}: {e}')
|
||||
except Exception as e:
|
||||
print(f' LIST FAIL: {e}')
|
||||
|
||||
# Pull 5 Corrected HVAS TXT samples
|
||||
print('\n=== Pulling Corrected HVAS samples ===')
|
||||
ch_dir_posix = 'C:/Shares/test/Corrected HVAS Files'
|
||||
try:
|
||||
entries = sorted(sftp.listdir(ch_dir_posix))
|
||||
print(f' {len(entries)} entries, pulling first 5')
|
||||
for name in entries[:5]:
|
||||
try:
|
||||
sftp.get(f'{ch_dir_posix}/{name}', os.path.join(LOCAL, 'corrected-hvas', name))
|
||||
print(f' pulled {name}')
|
||||
except Exception as e:
|
||||
print(f' MISS {name}: {e}')
|
||||
except Exception as e:
|
||||
print(f' LIST FAIL: {e}')
|
||||
|
||||
finally:
|
||||
sftp.close()
|
||||
c.close()
|
||||
|
||||
print('\n=== DONE ===')
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Categorize PASS/FAIL lines across the 14 local VASLOG .DAT samples.
|
||||
|
||||
Goal: understand whether plain-decimal vs E-notation correlates with
|
||||
file (model), date, or random distribution.
|
||||
"""
|
||||
import os, re
|
||||
|
||||
DAT_DIR = r'D:\claudetools\projects\dataforth-dos\datasheet-pipeline\scmvas-hvas-research\samples\vaslog-dat'
|
||||
|
||||
RE_PASS_SCI = re.compile(r'"(PASS|FAIL)\s*(-?\d+\.?\d*E[+-]?\d{2})(\d?)"', re.I)
|
||||
RE_PASS_PLAIN = re.compile(r'"(PASS|FAIL)\s*(-?\.?\d+\.?\d*)"', re.I)
|
||||
RE_SNDATE = re.compile(r'^"([^"]+)","(\d{2}-\d{2}-\d{4})"')
|
||||
|
||||
for fn in sorted(os.listdir(DAT_DIR)):
|
||||
path = os.path.join(DAT_DIR, fn)
|
||||
with open(path, 'r', encoding='latin-1') as f:
|
||||
lines = [l.strip() for l in f if l.strip()]
|
||||
sci = 0
|
||||
plain = 0
|
||||
other = 0
|
||||
dates = []
|
||||
model = None
|
||||
for line in lines:
|
||||
if line.startswith('"') and not line.startswith('"PASS') and not line.startswith('"FAIL') and ',' not in line and '0' not in line[1:3]:
|
||||
if not model: model = line.replace('"','').strip()
|
||||
m_snd = RE_SNDATE.match(line)
|
||||
if m_snd:
|
||||
dates.append(m_snd.group(2))
|
||||
continue
|
||||
# Only interested in lines that contain a PASS/FAIL status field (not the SN line)
|
||||
if '"PASS' in line or '"FAIL' in line:
|
||||
m_sci = RE_PASS_SCI.search(line)
|
||||
m_plain = RE_PASS_PLAIN.search(line)
|
||||
if m_sci: sci += 1
|
||||
elif m_plain: plain += 1
|
||||
else: other += 1
|
||||
# Sort dates by year
|
||||
dates_sorted = sorted(dates)
|
||||
date_range = f'{dates_sorted[0]} .. {dates_sorted[-1]}' if dates_sorted else '-'
|
||||
total = sci + plain + other
|
||||
print(f'{fn:20s} model={model!r:18s} total={total:4d} sci={sci:4d} plain={plain:4d} other={other:4d} dates={date_range}')
|
||||
@@ -0,0 +1,92 @@
|
||||
"""Parse hvin.dat based on TYPE DBASE from DBHV.BAS / TESTHV3.BAS.
|
||||
|
||||
Record layout (199 bytes each):
|
||||
MODNAME STRING * 13
|
||||
INTYPE STRING * 3
|
||||
MININ SINGLE
|
||||
MAXIN SINGLE
|
||||
OUTSIGTYPE STRING * 7
|
||||
MINOUT SINGLE
|
||||
MAXOUT SINGLE
|
||||
WAVESHPCAL STRING * 8
|
||||
...42 SINGLEs total...
|
||||
"""
|
||||
import struct, sys
|
||||
|
||||
FIELDS = [
|
||||
('MODNAME', 's', 13),
|
||||
('INTYPE', 's', 3),
|
||||
('MININ', 'f', 4),
|
||||
('MAXIN', 'f', 4),
|
||||
('OUTSIGTYPE', 's', 7),
|
||||
('MINOUT', 'f', 4),
|
||||
('MAXOUT', 'f', 4),
|
||||
('WAVESHPCAL', 's', 8),
|
||||
('FINCAL', 'f', 4),
|
||||
('FINMIN', 'f', 4),
|
||||
('FINMAX', 'f', 4),
|
||||
('FINEXTMIN', 'f', 4),
|
||||
('FINEXTMAX', 'f', 4),
|
||||
('INPROTECT', 'f', 4),
|
||||
('IOUTLIM', 'f', 4),
|
||||
('VOUTLIM', 'f', 4),
|
||||
('OUTRES', 'f', 4),
|
||||
('OUTNOISE', 'f', 4),
|
||||
('OSCALIN', 'f', 4),
|
||||
('GNCALIN', 'f', 4),
|
||||
('OSCALPT', 'f', 4),
|
||||
('GNCALPT', 'f', 4),
|
||||
('CALTOL', 'f', 4),
|
||||
('ADJ', 'f', 4),
|
||||
('LINEAR', 'f', 4),
|
||||
('ACCSINCAL', 'f', 4),
|
||||
('ACCSINSTD', 'f', 4),
|
||||
('ACCSINEXT', 'f', 4),
|
||||
('ACCCF12', 'f', 4),
|
||||
('ACCCF23', 'f', 4),
|
||||
('ACCCF34', 'f', 4),
|
||||
('ACCCF45', 'f', 4),
|
||||
('CMR', 'f', 4),
|
||||
('STEPTIME', 'f', 4),
|
||||
('STEPPERC', 'f', 4),
|
||||
('STEPTOL', 'f', 4),
|
||||
('LOOPVMIN', 'f', 4),
|
||||
('LOOPVNOM', 'f', 4),
|
||||
('LOOPVMAX', 'f', 4),
|
||||
('MAXLOADR', 'f', 4),
|
||||
('MINVS', 'f', 4),
|
||||
('NOMVS', 'f', 4),
|
||||
('MAXVS', 'f', 4),
|
||||
('ISMIN', 'f', 4),
|
||||
('ISMAX', 'f', 4),
|
||||
('PSS', 'f', 4),
|
||||
]
|
||||
|
||||
RECORD_SIZE = sum(sz for _, _, sz in FIELDS)
|
||||
print(f'Computed record size: {RECORD_SIZE} bytes')
|
||||
|
||||
def parse_record(buf, off):
|
||||
rec = {}
|
||||
pos = off
|
||||
for name, typ, sz in FIELDS:
|
||||
chunk = buf[pos:pos+sz]
|
||||
if typ == 's':
|
||||
rec[name] = chunk.rstrip(b'\x00 ').decode('latin-1', errors='replace').strip()
|
||||
else:
|
||||
rec[name] = struct.unpack('<f', chunk)[0]
|
||||
pos += sz
|
||||
return rec
|
||||
|
||||
def main():
|
||||
with open(sys.argv[1], 'rb') as f:
|
||||
buf = f.read()
|
||||
print(f'File size: {len(buf)} bytes, {len(buf)/RECORD_SIZE} records')
|
||||
n = len(buf) // RECORD_SIZE
|
||||
for i in range(n):
|
||||
r = parse_record(buf, i * RECORD_SIZE)
|
||||
if not r['MODNAME'] or not any(c.isalnum() for c in r['MODNAME']):
|
||||
continue
|
||||
print(f"#{i+1:02d} {r['MODNAME']!r:20s} IN={r['INTYPE']!r} OUT={r['OUTSIGTYPE']!r} {r['MININ']:+.3f} to {r['MAXIN']:+.3f} -> {r['MINOUT']:+.3f} to {r['MAXOUT']:+.3f} Vs={r['NOMVS']:.1f} Is={r['ISMAX']:.1f}mA")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-5
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.01% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-5
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.01% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-6
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.015% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-6
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.015% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-7
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy -0.011% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-7
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy -0.011% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-7
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy -0.011% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-7
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy -0.011% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-8
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.003% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-8
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.003% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-8
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.003% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-8
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.003% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-9
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.001% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-9
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.001% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-9
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.001% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04-09-2026
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 179377-9
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.001% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Tested By: ____________ QC: ________________
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04/09/2026
|
||||
Model: SCMHVAS-M0100
|
||||
SN: 179379-9
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.007% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 02/16/2011
|
||||
Model: SCMVAS-M700
|
||||
SN: 66260-12
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.012% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 08-01-2024
|
||||
Model: SCMHVAS-M0300
|
||||
SN: 171087-1
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.004% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 08-07-2024
|
||||
Model: SCMHVAS-M0300
|
||||
SN: 171087-10
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.01% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 08-07-2024
|
||||
Model: SCMHVAS-M0300
|
||||
SN: 171087-11
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.011% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 08-07-2024
|
||||
Model: SCMHVAS-M0300
|
||||
SN: 171087-12
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.007% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 08-07-2024
|
||||
Model: SCMHVAS-M0300
|
||||
SN: 171087-13
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.006% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 04/09/2026
|
||||
Model: SCMHVAS-M0100
|
||||
SN: 179379-1
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.007% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,396 @@
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-7.005501E-033","","",""
|
||||
"","","",""
|
||||
"179379-1","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.992997E-033","","",""
|
||||
"","","",""
|
||||
"179379-2","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.301449E-023","","",""
|
||||
"","","",""
|
||||
"179379-3","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.298487E-023","","",""
|
||||
"","","",""
|
||||
"179379-4","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.988644E-033","","",""
|
||||
"","","",""
|
||||
"179379-5","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.920448E-043","","",""
|
||||
"","","",""
|
||||
"179379-6","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.988085E-033","","",""
|
||||
"","","",""
|
||||
"179379-7","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.498721E-023","","",""
|
||||
"","","",""
|
||||
"179379-3","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 2.099555E-023","","",""
|
||||
"","","",""
|
||||
"179379-8","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-7.013697E-033","","",""
|
||||
"","","",""
|
||||
"179379-9","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 2.987589E-033","","",""
|
||||
"","","",""
|
||||
"179379-10","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.997374E-033","","",""
|
||||
"","","",""
|
||||
"179379-11","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.994659E-033","","",""
|
||||
"","","",""
|
||||
"179379-12","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS .01599373","","",""
|
||||
"","","",""
|
||||
"179379-13","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-2.996437E-033","","",""
|
||||
"","","",""
|
||||
"179379-14","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.298943E-023","","",""
|
||||
"","","",""
|
||||
"179379-15","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.098672E-023","","",""
|
||||
"","","",""
|
||||
"179379-16","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.986045E-033","","",""
|
||||
"","","",""
|
||||
"179379-17","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-2.00728E-033","","",""
|
||||
"","","",""
|
||||
"179379-18","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.005922E-033","","",""
|
||||
"","","",""
|
||||
"179379-19","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.988948E-033","","",""
|
||||
"","","",""
|
||||
"179379-20","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-5.011353E-033","","",""
|
||||
"","","",""
|
||||
"179379-21","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.998683E-023","","",""
|
||||
"","","",""
|
||||
"179379-22","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.799585E-023","","",""
|
||||
"","","",""
|
||||
"179379-23","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.563481E-063","","",""
|
||||
"","","",""
|
||||
"179379-24","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS .01198773","","",""
|
||||
"","","",""
|
||||
"179379-25","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 2.987217E-033","","",""
|
||||
"","","",""
|
||||
"179379-26","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.96422E-043","","",""
|
||||
"","","",""
|
||||
"179379-27","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.989933E-033","","",""
|
||||
"","","",""
|
||||
"179379-28","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.992649E-033","","",""
|
||||
"","","",""
|
||||
"179379-29","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.399023E-023","","",""
|
||||
"","","",""
|
||||
"179379-30","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.988575E-033","","",""
|
||||
"","","",""
|
||||
"179379-31","04-09-2026"
|
||||
"SCMHVAS-M0100 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.989746E-033","","",""
|
||||
"","","",""
|
||||
"179379-32","04-09-2026"
|
||||
@@ -0,0 +1,336 @@
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.000783E-033","","",""
|
||||
"","","",""
|
||||
"bri-1","08-23-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.000596E-033","","",""
|
||||
"","","",""
|
||||
"169815-6","08-28-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.492646E-033","","",""
|
||||
"","","",""
|
||||
"169815-7","08-28-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-2.328306E-073","","",""
|
||||
"","","",""
|
||||
"169815-8","08-28-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.005393E-043","","",""
|
||||
"","","",""
|
||||
"169815-9","08-28-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.997352E-033","","",""
|
||||
"","","",""
|
||||
"169815-10","08-28-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-7.498404E-033","","",""
|
||||
"","","",""
|
||||
"169815-11","08-28-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.500226E-023","","",""
|
||||
"","","",""
|
||||
"169815-12","08-28-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.498124E-033","","",""
|
||||
"","","",""
|
||||
"169815-13","08-28-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.500011E-033","","",""
|
||||
"","","",""
|
||||
"169815-14","08-28-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.549707E-023","","",""
|
||||
"","","",""
|
||||
"169815-15","08-28-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.001125E-033","","",""
|
||||
"","","",""
|
||||
"169815-16","08-28-2024"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.494461E-033","","",""
|
||||
"","","",""
|
||||
"173821-1","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.999649E-033","","",""
|
||||
"","","",""
|
||||
"173821-2","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.498047E-033","","",""
|
||||
"","","",""
|
||||
"173821-3","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.498047E-033","","",""
|
||||
"","","",""
|
||||
"173821-4","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.998291E-033","","",""
|
||||
"","","",""
|
||||
"173821-5","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.299912E-023","","",""
|
||||
"","","",""
|
||||
"173821-6","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.495518E-033","","",""
|
||||
"","","",""
|
||||
"173821-7","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.497705E-033","","",""
|
||||
"","","",""
|
||||
"173821-8","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.500709E-033","","",""
|
||||
"","","",""
|
||||
"173821-9","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.498047E-033","","",""
|
||||
"","","",""
|
||||
"173821-10","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.049563E-023","","",""
|
||||
"","","",""
|
||||
"173821-11","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-5.014241E-043","","",""
|
||||
"","","",""
|
||||
"173821-12","02-12-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.500011E-033","","",""
|
||||
"","","",""
|
||||
"175122-1","05-14-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-2.497574E-033","","",""
|
||||
"","","",""
|
||||
"175122-2","05-14-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.656613E-083","","",""
|
||||
"","","",""
|
||||
"175122-3","05-14-2025"
|
||||
"SCMHVAS-M0200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.005393E-043","","",""
|
||||
"","","",""
|
||||
"175122-4","05-14-2025"
|
||||
@@ -0,0 +1,348 @@
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.997034E-033","","",""
|
||||
"","","",""
|
||||
"bri-1","08-23-2024"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.665592E-033","","",""
|
||||
"","","",""
|
||||
"164434-28","08-28-2024"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.066744E-023","","",""
|
||||
"","","",""
|
||||
"164434-29","08-28-2024"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.99847E-033","","",""
|
||||
"","","",""
|
||||
"164434-30","08-28-2024"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 2.667214E-033","","",""
|
||||
"","","",""
|
||||
"164434-31","08-28-2024"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.666951E-033","","",""
|
||||
"","","",""
|
||||
"164434-32","08-28-2024"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.066665E-023","","",""
|
||||
"","","",""
|
||||
"175815-1","07-08-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.232908E-023","","",""
|
||||
"","","",""
|
||||
"175815-2","07-08-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.632855E-023","","",""
|
||||
"","","",""
|
||||
"175815-3","07-08-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.330595E-033","","",""
|
||||
"","","",""
|
||||
"175815-4","07-08-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.667478E-033","","",""
|
||||
"","","",""
|
||||
"175815-5","07-08-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.066695E-023","","",""
|
||||
"","","",""
|
||||
"175815-6","07-08-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS .01499823","","",""
|
||||
"","","",""
|
||||
"175815-7","07-08-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.299847E-023","","",""
|
||||
"","","",""
|
||||
"175815-8","07-08-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.32912E-033","","",""
|
||||
"","","",""
|
||||
"177428-1","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.328026E-033","","",""
|
||||
"","","",""
|
||||
"177428-2","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.133021E-023","","",""
|
||||
"","","",""
|
||||
"177428-3","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.328026E-033","","",""
|
||||
"","","",""
|
||||
"177428-4","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.301771E-043","","",""
|
||||
"","","",""
|
||||
"177428-5","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.099533E-023","","",""
|
||||
"","","",""
|
||||
"177428-6","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.066355E-023","","",""
|
||||
"","","",""
|
||||
"177428-7","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.299507E-023","","",""
|
||||
"","","",""
|
||||
"177428-8","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.663815E-033","","",""
|
||||
"","","",""
|
||||
"177428-9","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.995598E-033","","",""
|
||||
"","","",""
|
||||
"177428-10","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.664078E-033","","",""
|
||||
"","","",""
|
||||
"177428-11","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.665436E-033","","",""
|
||||
"","","",""
|
||||
"177428-12","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.330742E-033","","",""
|
||||
"","","",""
|
||||
"177428-13","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 2.899593E-023","","",""
|
||||
"","","",""
|
||||
"177428-14","11-11-2025"
|
||||
"SCMHVAS-M0300 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.995598E-033","","",""
|
||||
"","","",""
|
||||
"177428-15","11-11-2025"
|
||||
@@ -0,0 +1,60 @@
|
||||
"SCMHVAS-M0400 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.254585E-033","","",""
|
||||
"","","",""
|
||||
"bri-1","08-23-2024"
|
||||
"SCMHVAS-M0400 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.524978E-023","","",""
|
||||
"","","",""
|
||||
"168630-9","08-28-2024"
|
||||
"SCMHVAS-M0400 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.999184E-033","","",""
|
||||
"","","",""
|
||||
"168630-10","08-28-2024"
|
||||
"SCMHVAS-M0400 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.746245E-033","","",""
|
||||
"","","",""
|
||||
"168630-11","08-28-2024"
|
||||
"SCMHVAS-M0400 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.999713E-033","","",""
|
||||
"","","",""
|
||||
"168630-12","08-28-2024"
|
||||
@@ -0,0 +1,336 @@
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.089708E-063","","",""
|
||||
"","","",""
|
||||
"178647-1","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.501773E-063","","",""
|
||||
"","","",""
|
||||
"178647-2","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.501773E-063","","",""
|
||||
"","","",""
|
||||
"178647-3","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-.00499773","","",""
|
||||
"","","",""
|
||||
"178647-4","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-.00499773","","",""
|
||||
"","","",""
|
||||
"178647-5","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-.00499773","","",""
|
||||
"","","",""
|
||||
"178647-6","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.089708E-063","","",""
|
||||
"","","",""
|
||||
"178647-7","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.501773E-063","","",""
|
||||
"","","",""
|
||||
"178647-8","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.089708E-063","","",""
|
||||
"","","",""
|
||||
"178647-9","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.089708E-063","","",""
|
||||
"","","",""
|
||||
"178647-10","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.140901E-063","","",""
|
||||
"","","",""
|
||||
"178647-11","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.140901E-063","","",""
|
||||
"","","",""
|
||||
"178647-12","02-19-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.001798E-033","","",""
|
||||
"","","",""
|
||||
"179380-1","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.544854E-063","","",""
|
||||
"","","",""
|
||||
"179380-2","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.544854E-063","","",""
|
||||
"","","",""
|
||||
"179380-3","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.987344E-033","","",""
|
||||
"","","",""
|
||||
"179380-4","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.909272E-063","","",""
|
||||
"","","",""
|
||||
"179380-5","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.544854E-063","","",""
|
||||
"","","",""
|
||||
"179380-6","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.987344E-033","","",""
|
||||
"","","",""
|
||||
"179380-7","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.987344E-033","","",""
|
||||
"","","",""
|
||||
"179380-8","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.909272E-063","","",""
|
||||
"","","",""
|
||||
"179380-9","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.544854E-063","","",""
|
||||
"","","",""
|
||||
"179380-10","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.544854E-063","","",""
|
||||
"","","",""
|
||||
"179380-11","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.544854E-063","","",""
|
||||
"","","",""
|
||||
"179380-12","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.544854E-063","","",""
|
||||
"","","",""
|
||||
"179380-13","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.001798E-033","","",""
|
||||
"","","",""
|
||||
"179380-14","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.544854E-063","","",""
|
||||
"","","",""
|
||||
"179380-15","04-03-2026"
|
||||
"SCMHVAS-MPT "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-9.994134E-033","","",""
|
||||
"","","",""
|
||||
"179380-16","04-03-2026"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,540 @@
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.997057E-033","","",""
|
||||
"","","",""
|
||||
"-1","05-17-2015"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-3.502657E-033","","",""
|
||||
"","","",""
|
||||
"-2","05-17-2015"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.499941E-033","","",""
|
||||
"","","",""
|
||||
"-2","05-17-2015"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.005363E-033","","",""
|
||||
"","","",""
|
||||
"-1","05-17-2015"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.049749E-023","","",""
|
||||
"","","",""
|
||||
"160957-1","11-28-2022"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.006503E-033","","",""
|
||||
"","","",""
|
||||
"160957-2","11-28-2022"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.003258E-033","","",""
|
||||
"","","",""
|
||||
"160957-3","11-28-2022"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-7.505388E-033","","",""
|
||||
"","","",""
|
||||
"160957-4","11-28-2022"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-3.505917E-033","","",""
|
||||
"","","",""
|
||||
"160957-5","11-28-2022"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-5.502672E-033","","",""
|
||||
"","","",""
|
||||
"160957-6","11-28-2022"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-3.505917E-033","","",""
|
||||
"","","",""
|
||||
"160957-7","11-28-2022"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.50403E-033","","",""
|
||||
"","","",""
|
||||
"160957-8","11-28-2022"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.491249E-033","","",""
|
||||
"","","",""
|
||||
"162077-1","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-5.509052E-033","","",""
|
||||
"","","",""
|
||||
"162077-2","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-8.00658E-033","","",""
|
||||
"","","",""
|
||||
"162077-3","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-8.00658E-033","","",""
|
||||
"","","",""
|
||||
"162077-4","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-7.005408E-033","","",""
|
||||
"","","",""
|
||||
"162077-5","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.003864E-033","","",""
|
||||
"","","",""
|
||||
"162077-6","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.000333E-023","","",""
|
||||
"","","",""
|
||||
"162077-7","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-3.00575E-033","","",""
|
||||
"","","",""
|
||||
"162077-8","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.504636E-033","","",""
|
||||
"","","",""
|
||||
"162077-9","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-3.506522E-033","","",""
|
||||
"","","",""
|
||||
"162077-10","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.504636E-033","","",""
|
||||
"","","",""
|
||||
"162077-11","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.503806E-033","","",""
|
||||
"","","",""
|
||||
"162077-12","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-9.002164E-033","","",""
|
||||
"","","",""
|
||||
"162077-13","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-2.505164E-033","","",""
|
||||
"","","",""
|
||||
"162077-14","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.504636E-033","","",""
|
||||
"","","",""
|
||||
"162077-15","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.504636E-033","","",""
|
||||
"","","",""
|
||||
"162077-16","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-5.503278E-033","","",""
|
||||
"","","",""
|
||||
"162077-17","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.507881E-033","","",""
|
||||
"","","",""
|
||||
"162077-18","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-9.002164E-033","","",""
|
||||
"","","",""
|
||||
"162077-19","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.001521E-033","","",""
|
||||
"","","",""
|
||||
"162077-20","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-3.005937E-033","","",""
|
||||
"","","",""
|
||||
"162077-21","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-3.506709E-033","","",""
|
||||
"","","",""
|
||||
"162077-22","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-5.002692E-033","","",""
|
||||
"","","",""
|
||||
"162077-23","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.003221E-033","","",""
|
||||
"","","",""
|
||||
"162077-24","02-01-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-5.020294E-043","","",""
|
||||
"","","",""
|
||||
"163430-1","04-05-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.000729E-033","","",""
|
||||
"","","",""
|
||||
"163430-2","04-05-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-7.004803E-033","","",""
|
||||
"","","",""
|
||||
"163430-3","04-05-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 2.996856E-033","","",""
|
||||
"","","",""
|
||||
"163430-4","04-05-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-9.001559E-033","","",""
|
||||
"","","",""
|
||||
"163430-5","04-05-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.257285E-063","","",""
|
||||
"","","",""
|
||||
"163430-6","04-05-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.998214E-033","","",""
|
||||
"","","",""
|
||||
"163430-7","04-05-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-7.004803E-033","","",""
|
||||
"","","",""
|
||||
"163430-8","04-05-2023"
|
||||
"SCMVAS-M200 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-7.004989E-033","","",""
|
||||
"","","",""
|
||||
"163430-7","04-05-2023"
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,192 @@
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.000837E-033","","",""
|
||||
"","","",""
|
||||
"150251-1","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.800668E-033","","",""
|
||||
"","","",""
|
||||
"150251-2","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.200462E-033","","",""
|
||||
"","","",""
|
||||
"150251-3","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-7.800536E-033","","",""
|
||||
"","","",""
|
||||
"150251-4","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.601307E-033","","",""
|
||||
"","","",""
|
||||
"150251-5","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-3.600213E-033","","",""
|
||||
"","","",""
|
||||
"150251-6","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.398597E-033","","",""
|
||||
"","","",""
|
||||
"150251-7","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.400491E-033","","",""
|
||||
"","","",""
|
||||
"150251-8","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.800133E-033","","",""
|
||||
"","","",""
|
||||
"150251-9","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.399265E-033","","",""
|
||||
"","","",""
|
||||
"150251-10","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-5.400623E-033","","",""
|
||||
"","","",""
|
||||
"150251-11","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.201129E-033","","",""
|
||||
"","","",""
|
||||
"150251-12","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-3.200036E-033","","",""
|
||||
"","","",""
|
||||
"150251-13","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-7.200997E-033","","",""
|
||||
"","","",""
|
||||
"150251-14","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.800133E-033","","",""
|
||||
"","","",""
|
||||
"150251-15","06-08-2021"
|
||||
"SCMVAS-M500 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.798907E-033","","",""
|
||||
"","","",""
|
||||
"150251-16","06-08-2021"
|
||||
@@ -0,0 +1,348 @@
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.965756E-033","","",""
|
||||
"","","",""
|
||||
"145728-1","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.253222E-023","","",""
|
||||
"","","",""
|
||||
"145728-2","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.36673E-033","","",""
|
||||
"","","",""
|
||||
"145728-3","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.566802E-033","","",""
|
||||
"","","",""
|
||||
"145728-4","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.798674E-033","","",""
|
||||
"","","",""
|
||||
"145728-5","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.999575E-033","","",""
|
||||
"","","",""
|
||||
"145728-6","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.009941E-023","","",""
|
||||
"","","",""
|
||||
"145728-7","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.998481E-033","","",""
|
||||
"","","",""
|
||||
"145728-8","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS .00633343","","",""
|
||||
"","","",""
|
||||
"145787-1","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.698836E-033","","",""
|
||||
"","","",""
|
||||
"145787-2","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.965889E-033","","",""
|
||||
"","","",""
|
||||
"145787-3","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.666244E-033","","",""
|
||||
"","","",""
|
||||
"145787-4","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.499557E-033","","",""
|
||||
"","","",""
|
||||
"145787-5","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.129896E-023","","",""
|
||||
"","","",""
|
||||
"145787-6","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.599262E-033","","",""
|
||||
"","","",""
|
||||
"145787-7","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS .00783243","","",""
|
||||
"","","",""
|
||||
"145787-8","09-30-2020"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.230843E-033","","",""
|
||||
"","","",""
|
||||
"162080-1","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.699457E-033","","",""
|
||||
"","","",""
|
||||
"162080-2","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.931858E-033","","",""
|
||||
"","","",""
|
||||
"162080-3","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.131005E-033","","",""
|
||||
"","","",""
|
||||
"162080-4","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.131005E-033","","",""
|
||||
"","","",""
|
||||
"162080-5","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.731785E-033","","",""
|
||||
"","","",""
|
||||
"162080-6","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.531978E-033","","",""
|
||||
"","","",""
|
||||
"162080-7","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 3.630854E-033","","",""
|
||||
"","","",""
|
||||
"162080-8","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.631118E-033","","",""
|
||||
"","","",""
|
||||
"162080-9","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.498556E-033","","",""
|
||||
"","","",""
|
||||
"162080-10","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.432008E-033","","",""
|
||||
"","","",""
|
||||
"162080-11","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.765247E-033","","",""
|
||||
"","","",""
|
||||
"162080-12","01-25-2023"
|
||||
"SCMVAS-M600 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 5.264964E-033","","",""
|
||||
"","","",""
|
||||
"162080-12","01-25-2023"
|
||||
@@ -0,0 +1,276 @@
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-8.617003E-033","","",""
|
||||
"","","",""
|
||||
"95603-1","03-07-2014"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 6.376649E-033","","",""
|
||||
"","","",""
|
||||
"147728-1","01-21-2021"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.769826E-033","","",""
|
||||
"","","",""
|
||||
"147728-2","01-21-2021"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-3.423844E-033","","",""
|
||||
"","","",""
|
||||
"147728-3","01-21-2021"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.176841E-033","","",""
|
||||
"","","",""
|
||||
"147728-4","01-21-2021"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 7.077003E-033","","",""
|
||||
"","","",""
|
||||
"147728-5","01-21-2021"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.023267E-033","","",""
|
||||
"","","",""
|
||||
"147728-6","01-21-2021"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-5.523418E-033","","",""
|
||||
"","","",""
|
||||
"147728-7","01-21-2021"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 9.131152E-033","","",""
|
||||
"","","",""
|
||||
"147728-8","01-21-2021"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.269407E-033","","",""
|
||||
"","","",""
|
||||
"163321-1","04-04-2023"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-8.769822E-033","","",""
|
||||
"","","",""
|
||||
"163321-2","04-04-2023"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 2.277095E-033","","",""
|
||||
"","","",""
|
||||
"163321-3","04-04-2023"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 1.777906E-033","","",""
|
||||
"","","",""
|
||||
"163321-4","04-04-2023"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.422467E-033","","",""
|
||||
"","","",""
|
||||
"163321-5","04-04-2023"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 4.638289E-033","","",""
|
||||
"","","",""
|
||||
"163321-6","04-04-2023"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.922052E-033","","",""
|
||||
"","","",""
|
||||
"163321-7","04-04-2023"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-8.977029E-033","","",""
|
||||
"","","",""
|
||||
"163321-8","04-04-2023"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-1.103203E-023","","",""
|
||||
"","","",""
|
||||
"170355-1","05-23-2024"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-6.854254E-033","","",""
|
||||
"","","",""
|
||||
"170355-2","05-23-2024"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-5.246372E-033","","",""
|
||||
"","","",""
|
||||
"170351-1","05-23-2024"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-5.738064E-033","","",""
|
||||
"","","",""
|
||||
"170351-2","05-23-2024"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS-4.691281E-033","","",""
|
||||
"","","",""
|
||||
"170351-3","05-23-2024"
|
||||
"SCMVAS-M650 "
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0,0,0,0,""
|
||||
0
|
||||
"","","",""
|
||||
"","","",""
|
||||
"PASS 8.353917E-033","","",""
|
||||
"","","",""
|
||||
"170355-3","05-23-2024"
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 10/04/2023
|
||||
Model: SCMHVAS-M0200
|
||||
SN: 166590-1
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.007% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 10/04/2023
|
||||
Model: SCMHVAS-M0200
|
||||
SN: 166590-2
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.007% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 10/04/2023
|
||||
Model: SCMHVAS-M0200
|
||||
SN: 166590-3
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.005% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 10/04/2023
|
||||
Model: SCMHVAS-M0200
|
||||
SN: 166590-4
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.006% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 10/04/2023
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 166592-1
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.005% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 10/04/2023
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 166592-2
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.009% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 10/04/2023
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 166592-3
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.003% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 10/04/2023
|
||||
Model: SCMHVAS-M0700
|
||||
SN: 166592-4
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.008% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 10/04/2023
|
||||
Model: SCMHVAS-M1000
|
||||
SN: 166593-1
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.009% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Dataforth Corporation Phone number: (520) 741-1404
|
||||
3331 E. Hemisphere Loop Fax: (520) 741-0762
|
||||
Tucson, AZ 85706 USA Email: info@dataforth.com
|
||||
|
||||
|
||||
|
||||
|
||||
TEST DATA SHEET
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Date: 10/04/2023
|
||||
Model: SCMHVAS-M1000
|
||||
SN: 166593-2
|
||||
FINAL TEST RESULTS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Parameter Measured Value Specification Status
|
||||
================ ============== ============= ======
|
||||
Accuracy 0.005% +/- 0.03% PASS
|
||||
|
||||
_______________________________________________________________________
|
||||
Check List
|
||||
|
||||
Module Appearance: __X__ Mounting Screw: __X__
|
||||
|
||||
Pins Straight: __X__ Module Header: __X__
|
||||
|
||||
It is hereby certified that the above product is in conformance with
|
||||
all requirements to the extent specified. This product is not
|
||||
authorized or warranted for use in life support devices and/or systems.
|
||||
|
||||
* NIST traceable calibration certificates support Measured Value data.
|
||||
Calibration services are available through ANSI/NCSL Z540-1 and
|
||||
ISO Guide 25 Certified Metrology Labs.
|
||||
|
||||
|
||||
@@ -0,0 +1,731 @@
|
||||
'Database Modification Program for DSCA33
|
||||
'DIN rail signal conditioning modules
|
||||
'
|
||||
'AUTHOR: John Lehman
|
||||
'DATE: 08/30/99
|
||||
'
|
||||
' REVISION RECORD
|
||||
|
||||
'DATE APPR DESCRIPTION
|
||||
'---- ---- -----------
|
||||
'08/30/99 JL Create, start from DBDSC4.BAS
|
||||
'10/30/06 JL Added suggested P.S. voltage levels of 19-29V.
|
||||
|
||||
DECLARE FUNCTION CHANGE (SPEC!)
|
||||
DECLARE FUNCTION CHANGE2 (SPEC!, SCALE!)
|
||||
DECLARE FUNCTION MENU1% ()
|
||||
DECLARE FUNCTION MENU3% () 'Gets the module family
|
||||
DECLARE FUNCTION REPEAT$ ()
|
||||
DECLARE FUNCTION STRINGVAL% (A$)
|
||||
|
||||
DECLARE SUB MODDATA (SEL%)
|
||||
DECLARE SUB GETSPECS (SEL%)
|
||||
DECLARE SUB ENTDATA ()
|
||||
DECLARE SUB SAVEDATA (SEL%)
|
||||
DECLARE SUB SORTALL ()
|
||||
DECLARE SUB SORTDB (ENDFLAG%)
|
||||
|
||||
'Database Record defintion for the specifications
|
||||
TYPE DBASE
|
||||
MODNAME AS STRING * 13 'DSCA33-XXXX or SCM5B33-XXXX
|
||||
INTYPE AS STRING * 3 ''V' OR 'A'
|
||||
MININ AS SINGLE 'Minus F.S. input (V or I)
|
||||
MAXIN AS SINGLE 'Plus F.S. input (V or I)
|
||||
OUTSIGTYPE AS STRING * 7 ''VOLTAGE' or 'CURRENT'
|
||||
MINOUT AS SINGLE 'Minus F.S. output (V or I)
|
||||
MAXOUT AS SINGLE 'Plus F.S. output (V or I)
|
||||
WAVESHPCAL AS STRING * 8 ''SINE', 'SQUARE', 'TRIANGLE', 'ARBxxxxx'
|
||||
FINCAL AS SINGLE 'Calibration Frequency input (Hz)
|
||||
FINMIN AS SINGLE 'Minimum Std. Frequency input (Hz)
|
||||
FINMAX AS SINGLE 'Highest Std. Frequency input (Hz)
|
||||
FINEXTMIN AS SINGLE 'Minimum Extd. Frequency input (Hz)
|
||||
FINEXTMAX AS SINGLE 'Highest Extd. Frequency input (Hz)
|
||||
INPROTECT AS SINGLE 'Rated input Protection (V or I)
|
||||
IOUTLIM AS SINGLE 'Current output limit (mA)
|
||||
VOUTLIM AS SINGLE 'Voltage output limit (V)
|
||||
OUTRES AS SINGLE 'Output resistance
|
||||
OUTNOISE AS SINGLE 'Output noise/ripple 100kHz BW (rms)
|
||||
OSCALIN AS SINGLE 'Module input for offset calibration
|
||||
GNCALIN AS SINGLE 'Module input for gain calibration
|
||||
OSCALPT AS SINGLE 'Offset cal point (%span)
|
||||
GNCALPT AS SINGLE 'Gain cal point (%span)
|
||||
CALTOL AS SINGLE 'Calibration tolerance (%span)
|
||||
ADJ AS SINGLE 'User Adjustability Range (%span)
|
||||
LINEAR AS SINGLE 'Linearity (% span) at sine input and FINCAL
|
||||
ACCSINCAL AS SINGLE 'Sine accuracy (%span) at CALFIN
|
||||
ACCSINSTD AS SINGLE 'Sine accuracy (%span) from FINMIN to FIMMAX
|
||||
ACCSINEXT AS SINGLE 'Sine accuracy (%span) from FINEXTMIN to FINEXTMAX
|
||||
ACCCF12 AS SINGLE 'C.F. = 1 TO 2 accuracy (%span) at FINCAL
|
||||
ACCCF23 AS SINGLE 'C.F. = 2 TO 3 accuracy (%span) at FINCAL
|
||||
ACCCF34 AS SINGLE 'C.F. = 3 TO 4 accuracy (%span) at FINCAL
|
||||
ACCCF45 AS SINGLE 'C.F. = 4 TO 5 accuracy (%span) at FINCAL
|
||||
CMR AS SINGLE 'Common Mode Rejection (dB)
|
||||
STEPTIME AS SINGLE 'Test time for step response
|
||||
STEPPERC AS SINGLE '% F.S. @ STEPTIME
|
||||
STEPTOL AS SINGLE 'STEPPERC measurement tolerance
|
||||
LOOPVMIN AS SINGLE 'Output Loop Voltage min (V)
|
||||
LOOPVNOM AS SINGLE 'Output Loop Voltage nom (V)
|
||||
LOOPVMAX AS SINGLE 'Output Loop Voltage max (V)
|
||||
MAXLOADR AS SINGLE 'Maximum load resistance (ohms)
|
||||
MINVS AS SINGLE 'Lowest supply voltage (V)
|
||||
NOMVS AS SINGLE 'Nominal supply voltage (V)
|
||||
MAXVS AS SINGLE 'Highest supply voltage (V)
|
||||
ISMIN AS SINGLE 'Minimum suuply current (mA)
|
||||
ISMAX AS SINGLE 'Supply current, full load (mA)
|
||||
PSS AS SINGLE 'Power supply sensitivity (ppm/% or %/% Delta Vs)
|
||||
END TYPE
|
||||
|
||||
TYPE DBASE2
|
||||
RECNUM AS INTEGER
|
||||
MODNAME AS STRING * 13
|
||||
END TYPE
|
||||
|
||||
'define common variables
|
||||
COMMON SHARED /SAMPLE/ SPECS AS DBASE, SORTDATA1 AS DBASE2, SORTDATA2 AS DBASE
|
||||
|
||||
DO
|
||||
SELECT CASE MENU1%
|
||||
|
||||
CASE 1
|
||||
CALL ENTDATA
|
||||
IF LEFT$(SPECS.MODNAME, 4) <> " " THEN
|
||||
CALL SAVEDATA(0)
|
||||
END IF
|
||||
CASE 2
|
||||
CALL GETSPECS(SEL%)
|
||||
IF LEFT$(SPECS.MODNAME, 4) <> "EXIT" THEN
|
||||
CALL MODDATA(SEL%)
|
||||
CALL SAVEDATA(0)
|
||||
END IF
|
||||
CASE 3
|
||||
CALL GETSPECS(SEL%)
|
||||
IF LEFT$(SPECS.MODNAME, 4) <> "EXIT" THEN
|
||||
CALL MODDATA(SEL%)
|
||||
CALL SAVEDATA(SEL%)
|
||||
END IF
|
||||
CASE 4
|
||||
CALL SORTALL
|
||||
CASE 5
|
||||
END
|
||||
END SELECT
|
||||
LOOP WHILE REPEAT$ <> "N"
|
||||
|
||||
FUNCTION CHANGE (SPEC!)
|
||||
|
||||
Y% = CSRLIN
|
||||
X% = POS(0)
|
||||
LOCATE Y%, X% + 1
|
||||
|
||||
INPUT A$
|
||||
|
||||
IF A$ <> "" THEN
|
||||
CHANGE = VAL(A$)
|
||||
ELSE
|
||||
CHANGE = SPEC!
|
||||
END IF
|
||||
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION CHANGE2 (SPEC!, SCALE!)
|
||||
|
||||
Y% = CSRLIN
|
||||
X% = POS(0)
|
||||
LOCATE Y%, X% + 1
|
||||
|
||||
INPUT A$
|
||||
|
||||
IF A$ <> "" THEN
|
||||
CHANGE2 = VAL(A$) / SCALE!
|
||||
ELSE
|
||||
CHANGE2 = SPEC!
|
||||
END IF
|
||||
|
||||
END FUNCTION
|
||||
|
||||
SUB ENTDATA
|
||||
|
||||
'for specs which are not input for all models, default value is zero.
|
||||
|
||||
CLS
|
||||
INPUT "SCM5B, SCM8B or DSCA Model Number (SCM5Bxx-xx, SCM8Bxx-xx or DSCAxx-xx, blank to exit) "; MN$
|
||||
|
||||
IF LEFT$(MN$, 1) = " " THEN
|
||||
EXIT SUB
|
||||
ELSE 'Check if model already exists
|
||||
OPEN "T:\ENGR\ATE\ATE\HVU\HVDATA\HVIN.DAT" FOR RANDOM AS #1 LEN = LEN(SPECS)
|
||||
|
||||
|
||||
|
||||
NR% = LOF(1) / LEN(SPECS)
|
||||
|
||||
FOR N = 1 TO NR%
|
||||
GET #1, N, SPECS
|
||||
IF SPECS.MODNAME = MN$ THEN
|
||||
BEEP
|
||||
PRINT "This model already exists in the database."
|
||||
PRINT "Press any key to continue."
|
||||
DO
|
||||
LOOP WHILE INKEY$ = ""
|
||||
CLOSE #1
|
||||
EXIT SUB
|
||||
END IF
|
||||
NEXT N
|
||||
CLOSE #1
|
||||
SPECS.MODNAME = UCASE$(MN$)
|
||||
END IF
|
||||
|
||||
PRINT "Valid Sensor Types:"
|
||||
PRINT TAB(5); "V (millivolt or volt source)"
|
||||
' PRINT TAB(5); "A (milliamp or amp source)"
|
||||
DO
|
||||
INPUT "Sensor Type "; SPECS.INTYPE
|
||||
LOOP WHILE LEFT$(UCASE$(SPECS.INTYPE), 1) <> "V" AND LEFT$(UCASE$(SPECS.INTYPE), 1) <> "A"
|
||||
|
||||
INPUT "Minus F.S. Input (V, A) "; SPECS.MININ
|
||||
INPUT "Plus F.S. Input (V, A) "; SPECS.MAXIN
|
||||
|
||||
DO
|
||||
INPUT "Output signal type ('VOLTAGE' or 'CURRENT')"; SPECS.OUTSIGTYPE
|
||||
SPECS.OUTSIGTYPE$ = UCASE$(SPECS.OUTSIGTYPE)
|
||||
LOOP WHILE SPECS.OUTSIGTYPE <> "VOLTAGE" AND SPECS.OUTSIGTYPE <> "CURRENT"
|
||||
|
||||
IF SPECS.OUTSIGTYPE = "CURRENT" THEN
|
||||
UNIT$ = "mA"
|
||||
SCALE! = 1000!
|
||||
ELSE
|
||||
UNIT$ = "V"
|
||||
SCALE! = 1!
|
||||
END IF
|
||||
|
||||
PRINT "Minus F.S. Output ("; UNIT$; ") ";
|
||||
INPUT SPECS.MINOUT
|
||||
SPECS.MINOUT = SPECS.MINOUT / SCALE! 'Convert to V or A
|
||||
PRINT "Plus F.S. Output ("; UNIT$; ") ";
|
||||
INPUT SPECS.MAXOUT
|
||||
SPECS.MAXOUT = SPECS.MAXOUT / SCALE! 'Convert to V or A
|
||||
|
||||
' DO
|
||||
' INPUT "Calibration Signal Waveshape (SINE, SQUARE, TRIANGLE, RAMP) = "; SPECS.WAVESHPCAL
|
||||
' SPECS.WAVESHPCAL = UCASE$(SPECS.WAVESHPCAL)
|
||||
'LOOP WHILE SPECS.WAVESHPCAL <> "SINE " AND SPECS.WAVESHPCAL <> "SQUARE " AND SPECS.WAVESHPCAL <> "TRIANGLE" AND SPECS.WAVESHPCAL <> "RAMP "
|
||||
|
||||
' INPUT "Calibration Signal Frequency (Hz, 60Hz standard) "; SPECS.FINCAL
|
||||
' INPUT "Mininum Standard Input Frequency (Hz) "; SPECS.FINMIN
|
||||
' INPUT "Maximum Standard Input Frequency (Hz) "; SPECS.FINMAX
|
||||
' INPUT "Mininum Extended Input Frequency (Hz) "; SPECS.FINEXTMIN
|
||||
' INPUT "Maximum Extended Input Frequency (Hz) "; SPECS.FINEXTMAX
|
||||
|
||||
'INPUT "Input Protection Rating, Max (V, A) "; SPECS.INPROTECT
|
||||
'IF SPECS.OUTSIGTYPE = "CURRENT" THEN
|
||||
' INPUT "Current Output Limit (mA) "; SPECS.IOUTLIM
|
||||
'ELSE
|
||||
' INPUT "Voltage Output Limit (V) "; SPECS.VOUTLIM
|
||||
'END IF
|
||||
|
||||
'IF LEFT$(SPECS.MODNAME, 5) = "SCM5B" THEN
|
||||
' INPUT "Output Resistance (Ohms) "; SPECS.OUTRES
|
||||
'END IF
|
||||
INPUT "Output noise/ripple (% span rms) "; SPECS.OUTNOISE
|
||||
INPUT "Input for OFFSET calibration (V, A) "; SPECS.OSCALIN
|
||||
INPUT "Input for GAIN calibration (V, A) "; SPECS.GNCALIN
|
||||
INPUT "Offset Calibration Point (+/- % of span) "; SPECS.OSCALPT
|
||||
INPUT "Gain Calibration Point (+/- % of span) "; SPECS.GNCALPT
|
||||
INPUT "Offset and Gain Calibration Tolerance (+/- % of Span) "; SPECS.CALTOL
|
||||
INPUT "Offset/Gain User Adjustability (+/- % of Span) "; SPECS.ADJ
|
||||
|
||||
INPUT "Nonlinearity (+/- % of span) "; SPECS.LINEAR
|
||||
'PRINT USING "Accuracy z (+/- % of span) "; SPECS.FINCAL
|
||||
INPUT "Accuracy (+/- % of span) "; SPECS.ACCSINCAL
|
||||
' PRINT USING "Accuracy w/ Sine Input @ #####Hz to #####Hz (+/- % of span) "; SPECS.FINMIN; SPECS.FINMAX
|
||||
' INPUT SPECS.ACCSINSTD
|
||||
' PRINT USING "Accuracy w/ Sine Input @ #####Hz to #####Hz (+/- % of span) "; SPECS.FINEXTMIN; SPECS.FINEXTMAX
|
||||
' INPUT SPECS.ACCSINEXT
|
||||
|
||||
' PRINT USING "Accuracy w/ Crest Factor 1 to 2 @ #####Hz (+/- % of span) "; SPECS.FINCAL
|
||||
'INPUT SPECS.ACCCF12
|
||||
'PRINT USING "Accuracy w/ Crest Factor 2 to 3 @ #####Hz (+/- % of span) "; SPECS.FINCAL
|
||||
' INPUT SPECS.ACCCF23
|
||||
' PRINT USING "Accuracy w/ Crest Factor 3 to 4 @ #####Hz (+/- % of span) "; SPECS.FINCAL
|
||||
' INPUT SPECS.ACCCF34
|
||||
' PRINT USING "Accuracy w/ Crest Factor 4 to 5 @ #####Hz (+/- % of span) "; SPECS.FINCAL
|
||||
' INPUT SPECS.ACCCF45
|
||||
|
||||
' INPUT "Common Mode Rejection (dB) "; SPECS.CMR
|
||||
INPUT "Step Response Test Delay (s, 0.1 typ.)"; SPECS.STEPTIME
|
||||
PRINT "Nominal Response at #.### s (% span)"; SPECS.STEPTIME;
|
||||
' INPUT SPECS.STEPPERC
|
||||
' INPUT "Step Response Tolerance (%) "; SPECS.STEPTOL
|
||||
|
||||
IF SPECS.OUTSIGTYPE = "CURRENT" THEN
|
||||
INPUT "Maximum Load Resistance (Ohms) "; SPECS.MAXLOADR
|
||||
IF LEFT$(SPECS.MODNAME, 5) = "SCM5B" THEN
|
||||
INPUT "Minimum Output Loop Voltage (V) "; SPECS.LOOPVMIN
|
||||
INPUT "Nominal Output Loop Voltage (V) "; SPECS.LOOPVNOM
|
||||
INPUT "Maximum Output Loop Voltage (V) "; SPECS.LOOPVMAX
|
||||
END IF
|
||||
END IF
|
||||
|
||||
INPUT "Minimum Supply Voltage (V) [5B/8B- 4.75V, DSCA- 19V]"; SPECS.MINVS
|
||||
INPUT "Nominal Supply Voltage (V) [5B/8B- 5V, DSCA- 24V]"; SPECS.NOMVS
|
||||
INPUT "Maximum Supply Voltage (V) [5B/8B- 5.25V, DSCA- 29V]"; SPECS.MAXVS
|
||||
INPUT "Minimum Supply Current (mA) "; SPECS.ISMIN
|
||||
INPUT "Maximum Supply Current (mA) "; SPECS.ISMAX
|
||||
|
||||
|
||||
IF LEFT$(SPECS.MODNAME, 5) = "SCM5B" THEN
|
||||
UNIT$ = "ppm / %"
|
||||
ELSEIF LEFT$(SPECS.MODNAME, 2) = "8B" THEN
|
||||
UNIT$ = "ppm / %"
|
||||
ELSE
|
||||
UNIT$ = "% / %"
|
||||
END IF
|
||||
PRINT "Power Supply Sensitivity ("; UNIT$; ") ";
|
||||
INPUT SPECS.PSS
|
||||
|
||||
END SUB
|
||||
|
||||
SUB GETSPECS (SEL%)
|
||||
|
||||
DIM POINTER%(1000)
|
||||
|
||||
CALL SORTDB(ENDFLAG%)
|
||||
IF ENDFLAG% = 1 THEN
|
||||
SPECS.MODNAME = "EXIT"
|
||||
EXIT SUB
|
||||
END IF
|
||||
|
||||
CLS
|
||||
LOCATE , 30
|
||||
PRINT "Model Selection Menu"
|
||||
PRINT TAB(30); "--------------------"
|
||||
PRINT
|
||||
|
||||
YINIT% = CSRLIN 'Initialize starting rows
|
||||
|
||||
OPEN "T:\ENGR\ATE\HVU\HVDATA\HVSORT.DAT" FOR RANDOM AS #2 LEN = LEN(SORTDATA1)
|
||||
|
||||
DO
|
||||
I% = I% + 1
|
||||
GET #2, I%, SORTDATA1
|
||||
IF SORTDATA1.RECNUM <> -1 THEN NUMRECORD! = NUMRECORD! + 1
|
||||
LOOP WHILE SORTDATA1.RECNUM <> -1
|
||||
|
||||
SCRNCTR% = 40 'display center
|
||||
NUMCHAR% = 19 '13 char + 4 char for # + 2 spaces
|
||||
NUMLINES! = 19 '# of lines for model display below header
|
||||
NUMCOLUMNS! = 1 + INT(NUMRECORD! / NUMLINES!)
|
||||
'Model # length = 20 characters max
|
||||
'-> 3 columns max per screen
|
||||
'3 columns x 26 char = 78 spaces
|
||||
|
||||
I% = 1
|
||||
|
||||
FOR C% = 0 TO NUMCOLUMNS! - 1
|
||||
Y% = YINIT%
|
||||
TB% = SCRNCTR% - NUMCHAR% / 2 * NUMCOLUMNS! + NUMCHAR% * C%
|
||||
DO
|
||||
GET #2, I%, SORTDATA1
|
||||
IF SORTDATA1.RECNUM <> -1 THEN
|
||||
POINTER%(I%) = SORTDATA1.RECNUM
|
||||
LOCATE Y%, TB%
|
||||
PRINT USING "##.) &"; I%; SORTDATA1.MODNAME
|
||||
I% = I% + 1
|
||||
Y% = Y% + 1
|
||||
END IF
|
||||
LOOP WHILE SORTDATA1.RECNUM <> -1 AND Y% - YINIT% < NUMLINES!
|
||||
|
||||
NEXT C%
|
||||
|
||||
LOCATE Y%, TB%
|
||||
PRINT USING "##.) Exit"; I%
|
||||
|
||||
CLOSE #2
|
||||
|
||||
DO
|
||||
LOCATE 23, SCRNCTR% - NUMCHAR% / 2 * NUMCOLUMNS!
|
||||
PRINT "Enter Selection ";
|
||||
INPUT SEL%
|
||||
LOOP WHILE SEL% < 1 OR SEL% > I%
|
||||
|
||||
IF SEL% = I% THEN
|
||||
SPECS.MODNAME = "EXIT"
|
||||
ELSE
|
||||
OPEN "T:\ENGR\ATE\HVU\HVDATA\HVIN.DAT" FOR RANDOM AS #1 LEN = LEN(SPECS)
|
||||
|
||||
GET #1, POINTER%(SEL%), SPECS
|
||||
CLOSE #1
|
||||
END IF
|
||||
|
||||
SEL% = POINTER%(SEL%) 'pass back to main code for use in SAVEDATA
|
||||
|
||||
END SUB
|
||||
|
||||
FUNCTION MENU1%
|
||||
|
||||
CLS
|
||||
LOCATE 5
|
||||
PRINT TAB(18); "SCM5Bxx, SCM8Bxx & DSCAxx DATABASE MODIFICATION PROGRAM"
|
||||
PRINT
|
||||
PRINT TAB(17); "1.) Add New Module, Clean Start"
|
||||
PRINT TAB(17); "2.) Add New Module, Start With Existing Module"
|
||||
PRINT TAB(17); "3.) Modify Existing Module"
|
||||
PRINT TAB(17); "4.) Sort Database"
|
||||
PRINT TAB(17); "5.) Exit Program"
|
||||
PRINT
|
||||
PRINT TAB(17); "Enter your selection";
|
||||
|
||||
DO
|
||||
A$ = INKEY$
|
||||
LOOP WHILE VAL(A$) < 1 OR VAL(A$) > 5
|
||||
|
||||
MENU1% = VAL(A$)
|
||||
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION MENU3%
|
||||
|
||||
CLS
|
||||
LOCATE 3, 26
|
||||
PRINT "Module Family Selection Menu"
|
||||
PRINT TAB(26); "---------------------------"
|
||||
PRINT
|
||||
PRINT TAB(34); "1.) SCM5Bxx-xx"
|
||||
PRINT TAB(34); "2.) DSCAxx-xx"
|
||||
PRINT TAB(34); "3.) 8Bxx-xx"
|
||||
PRINT TAB(34); "4.) Exit"
|
||||
|
||||
DO
|
||||
I$ = INKEY$
|
||||
LOOP WHILE VAL(I$) < 1 OR VAL(I$) > 4
|
||||
|
||||
MENU3% = VAL(I$)
|
||||
|
||||
END FUNCTION
|
||||
|
||||
SUB MODDATA (SEL%)
|
||||
|
||||
CLS
|
||||
PRINT "Model Number: "; SPECS.MODNAME;
|
||||
INPUT MN$
|
||||
IF MN$ <> "" THEN
|
||||
'Check if model already exists
|
||||
OPEN "T:\ENGR\ATE\HVU\HVDATA\HVIN.DAT" FOR RANDOM AS #1 LEN = LEN(SPECS)
|
||||
|
||||
NR% = LOF(1) / LEN(SPECS)
|
||||
|
||||
FOR N% = 1 TO NR%
|
||||
GET #1, N%, SPECS
|
||||
IF SPECS.MODNAME = MN$ THEN
|
||||
BEEP
|
||||
PRINT "This model already exists in the database."
|
||||
PRINT "Press any key to continue."
|
||||
DO
|
||||
LOOP WHILE INKEY$ = ""
|
||||
CLOSE #1
|
||||
EXIT SUB
|
||||
END IF
|
||||
NEXT
|
||||
GET #1, SEL%, SPECS
|
||||
SPECS.MODNAME = MN$
|
||||
CLOSE #1
|
||||
END IF
|
||||
|
||||
PRINT "Sensor Type: "; SPECS.INTYPE;
|
||||
INPUT A$
|
||||
IF A$ <> "" THEN
|
||||
SPECS.INTYPE = A$
|
||||
END IF
|
||||
|
||||
PRINT USING "Minus F.S. Input = +###.### (V, A)"; SPECS.MININ;
|
||||
SPECS.MININ = CHANGE(SPECS.MININ)
|
||||
PRINT USING "Plus F.S. Input = +###.### (V, A)"; SPECS.MAXIN;
|
||||
SPECS.MAXIN = CHANGE(SPECS.MAXIN)
|
||||
|
||||
DO
|
||||
PRINT "Output signal type = "; SPECS.OUTSIGTYPE;
|
||||
INPUT A$
|
||||
IF A$ <> "" THEN SPECS.OUTSIGTYPE = UCASE$(A$)
|
||||
LOOP WHILE SPECS.OUTSIGTYPE <> "VOLTAGE" AND SPECS.OUTSIGTYPE <> "CURRENT"
|
||||
|
||||
IF SPECS.OUTSIGTYPE = "CURRENT" THEN
|
||||
UNIT$ = "mA"
|
||||
SCALE! = 1000!
|
||||
ELSE
|
||||
UNIT$ = "V"
|
||||
SCALE! = 1!
|
||||
END IF
|
||||
|
||||
PRINT USING "Minus F.S. Output = +#####.## &"; SPECS.MINOUT * SCALE!; UNIT$;
|
||||
SPECS.MINOUT = CHANGE2(SPECS.MINOUT, SCALE!)
|
||||
PRINT USING "Plus F.S. Output = +#####.## &"; SPECS.MAXOUT * SCALE!; UNIT$;
|
||||
SPECS.MAXOUT = CHANGE2(SPECS.MAXOUT, SCALE!)
|
||||
|
||||
' DO
|
||||
' PRINT "Calibration Signal Waveshape (SINE, SQUARE, TRIANGLE, RAMP) = "; SPECS.WAVESHPCAL
|
||||
' INPUT A$
|
||||
' IF A$ <> "" THEN SPECS.WAVESHPCAL = UCASE$(A$)
|
||||
'LOOP WHILE SPECS.WAVESHPCAL <> "SINE " AND SPECS.WAVESHPCAL <> "SQUARE " AND SPECS.WAVESHPCAL <> "TRIANGLE" AND SPECS.WAVESHPCAL <> "RAMP "
|
||||
|
||||
' PRINT USING "Calibration Signal Frequency = ###### Hz"; SPECS.FINCAL;
|
||||
' SPECS.FINCAL = CHANGE(SPECS.FINCAL)
|
||||
' PRINT USING "Mininum Standard Input Frequency = ##### Hz"; SPECS.FINMIN;
|
||||
' SPECS.FINMIN = CHANGE(SPECS.FINMIN)
|
||||
' PRINT USING "Maximum Standard Input Frequency = ##### Hz"; SPECS.FINMAX;
|
||||
' SPECS.FINMAX = CHANGE(SPECS.FINMAX)
|
||||
' PRINT USING "Mininum Extended Input Frequency = ##### Hz"; SPECS.FINEXTMIN;
|
||||
' SPECS.FINEXTMIN = CHANGE(SPECS.FINEXTMIN)
|
||||
' PRINT USING "Maximum Extended Input Frequency = ###### Hz"; SPECS.FINEXTMAX;
|
||||
' SPECS.FINEXTMAX = CHANGE(SPECS.FINEXTMAX)
|
||||
|
||||
' PRINT USING "Input Protection Rating, Max = #### (V or A)"; SPECS.INPROTECT;
|
||||
' SPECS.INPROTECT = CHANGE(SPECS.INPROTECT)
|
||||
' IF SPECS.OUTSIGTYPE = "CURRENT" THEN
|
||||
' PRINT USING "Current Output Limit = ##.# mA"; SPECS.IOUTLIM;
|
||||
' SPECS.IOUTLIM = CHANGE(SPECS.IOUTLIM)
|
||||
'ELSE
|
||||
' PRINT USING "Voltage Output Limit = ##.# V"; SPECS.VOUTLIM;
|
||||
' SPECS.VOUTLIM = CHANGE(SPECS.VOUTLIM)
|
||||
'END IF
|
||||
|
||||
'IF LEFT$(SPECS.MODNAME, 5) = "SCM5B" THEN
|
||||
' PRINT USING "Output Resistance = ### Ohms"; SPECS.OUTRES;
|
||||
' SPECS.OUTRES = CHANGE(SPECS.OUTRES)
|
||||
'END IF
|
||||
|
||||
PRINT USING "Output Noise = #.### % span rms"; SPECS.OUTNOISE;
|
||||
SPECS.OUTNOISE = CHANGE(SPECS.OUTNOISE)
|
||||
|
||||
PRINT USING "Input for OFFSET calibration = +###.### (V, A)"; SPECS.OSCALIN;
|
||||
SPECS.OSCALIN = CHANGE(SPECS.OSCALIN)
|
||||
PRINT USING "Input for GAIN calibration = +###.### (V, A)"; SPECS.GNCALIN;
|
||||
SPECS.GNCALIN = CHANGE(SPECS.GNCALIN)
|
||||
|
||||
PRINT
|
||||
PRINT "Offset and gain may be calibrated at a point other than -f.s. out"
|
||||
PRINT "or +f.s. out."
|
||||
PRINT USING "Offset Calibration Point = +#.### % of span"; SPECS.OSCALPT;
|
||||
SPECS.OSCALPT = CHANGE(SPECS.OSCALPT)
|
||||
PRINT USING "Gain Calibration Point = +#.### % of span"; SPECS.GNCALPT;
|
||||
SPECS.GNCALPT = CHANGE(SPECS.GNCALPT)
|
||||
PRINT USING "Offset and Gain Calibration Tolerance = +/- #.### % of span"; SPECS.CALTOL;
|
||||
SPECS.CALTOL = CHANGE(SPECS.CALTOL)
|
||||
PRINT USING "Offset/Gain User Adjustability = +/- ##.# % of span"; SPECS.ADJ;
|
||||
SPECS.ADJ = CHANGE(SPECS.ADJ)
|
||||
|
||||
PRINT USING "Nonlinearity = +/- #.### % of span"; SPECS.LINEAR;
|
||||
SPECS.LINEAR = CHANGE(SPECS.LINEAR)
|
||||
PRINT USING "Accuracy = +/- #.### % of span"; SPECS.ACCSINCAL;
|
||||
SPECS.ACCSINCAL = CHANGE(SPECS.ACCSINCAL)
|
||||
' PRINT USING "Addt'l Error w/ Sine Input @ ##### Hz to ##### Hz = +/- #.### % of span"; SPECS.FINMIN; SPECS.FINMAX; SPECS.ACCSINSTD;
|
||||
' SPECS.ACCSINSTD = CHANGE(SPECS.ACCSINSTD)
|
||||
' PRINT USING "Addt'l Error w/ Sine Input @ ##### Hz to ##### Hz = +/- #.### % of span"; SPECS.FINEXTMIN; SPECS.FINEXTMAX; SPECS.ACCSINEXT;
|
||||
' SPECS.ACCSINEXT = CHANGE(SPECS.ACCSINEXT)
|
||||
|
||||
' PRINT USING "Addt'l Error w/ Crest Factor 1 to 2 @ ##### Hz = +/- #.### % of span"; SPECS.FINCAL; SPECS.ACCCF12;
|
||||
' SPECS.ACCCF12 = CHANGE(SPECS.ACCCF12)
|
||||
' PRINT USING "Addt'l Error w/ Crest Factor 2 to 3 @ ##### Hz = +/- #.### % of span"; SPECS.FINCAL; SPECS.ACCCF23;
|
||||
' SPECS.ACCCF23 = CHANGE(SPECS.ACCCF23)
|
||||
' PRINT USING "Addt'l Error w/ Crest Factor 3 to 4 @ ##### Hz = +/- #.### % of span"; SPECS.FINCAL; SPECS.ACCCF34;
|
||||
' SPECS.ACCCF34 = CHANGE(SPECS.ACCCF34)
|
||||
' PRINT USING "Addt'l Error w/ Crest Factor 4 to 5 @ ##### Hz = +/- #.### % of span"; SPECS.FINCAL; SPECS.ACCCF45;
|
||||
' SPECS.ACCCF45 = CHANGE(SPECS.ACCCF45)
|
||||
|
||||
' PRINT USING "Common Mode Rejection = ### dB"; SPECS.CMR;
|
||||
' SPECS.CMR = CHANGE(SPECS.CMR)
|
||||
PRINT USING "Step Response Test Delay = #.###### s"; SPECS.STEPTIME;
|
||||
SPECS.STEPTIME = CHANGE(SPECS.STEPTIME)
|
||||
' PRINT USING "Nominal Response at #.### s = ###.# % span"; SPECS.STEPTIME; SPECS.STEPPERC;
|
||||
'SPECS.STEPPERC = CHANGE(SPECS.STEPPERC)
|
||||
'PRINT USING "Step Response Tolerance = +/- ##.# % "; SPECS.STEPTOL;
|
||||
'SPECS.STEPTOL = CHANGE(SPECS.STEPTOL)
|
||||
|
||||
IF SPECS.OUTSIGTYPE = "CURRENT" THEN
|
||||
PRINT USING "Maximum Load Resistance = ##### Ohms"; SPECS.MAXLOADR;
|
||||
SPECS.MAXLOADR = CHANGE(SPECS.MAXLOADR)
|
||||
IF LEFT$(SPECS.MODNAME, 5) = "SCM5B" THEN
|
||||
PRINT USING "Minimum Output Loop Voltage = ##.# V"; SPECS.LOOPVMIN;
|
||||
SPECS.LOOPVMIN = CHANGE(SPECS.LOOPVMIN)
|
||||
PRINT USING "Nominal Output Loop Voltage = ##.# V"; SPECS.LOOPVNOM;
|
||||
SPECS.LOOPVNOM = CHANGE(SPECS.LOOPVNOM)
|
||||
PRINT USING "Maximum Output Loop Voltage = ##.# V"; SPECS.LOOPVMAX;
|
||||
SPECS.LOOPVMAX = CHANGE(SPECS.LOOPVMAX)
|
||||
END IF
|
||||
END IF
|
||||
|
||||
PRINT USING "Minimum Supply Voltage = ##.## V"; SPECS.MINVS;
|
||||
SPECS.MINVS = CHANGE(SPECS.MINVS)
|
||||
PRINT USING "Nominal Supply Voltage = ##.## V"; SPECS.NOMVS;
|
||||
SPECS.MINVS = CHANGE(SPECS.MINVS)
|
||||
PRINT USING "Maximum Supply Voltage = ##.## V"; SPECS.MAXVS;
|
||||
SPECS.MAXVS = CHANGE(SPECS.MAXVS)
|
||||
|
||||
PRINT USING "Minimum Supply Current = ### mA"; SPECS.ISMIN;
|
||||
SPECS.ISMIN = CHANGE(SPECS.ISMIN)
|
||||
PRINT USING "Maximum Supply Current = ### mA"; SPECS.ISMAX;
|
||||
SPECS.ISMAX = CHANGE(SPECS.ISMAX)
|
||||
|
||||
IF LEFT$(SPECS.MODNAME, 5) = "SCM5B" THEN
|
||||
PRINT USING "Power Supply Sensitivity = #### ppm / %"; SPECS.PSS;
|
||||
ELSEIF LEFT$(SPECS.MODNAME, 2) = "8B" THEN
|
||||
PRINT USING "Power Supply Sensitivity = #### ppm / %"; SPECS.PSS;
|
||||
|
||||
ELSE
|
||||
PRINT USING "Power Supply Sensitivity = #.#### % / %"; SPECS.PSS;
|
||||
END IF
|
||||
SPECS.PSS = CHANGE(SPECS.PSS)
|
||||
|
||||
END SUB
|
||||
|
||||
FUNCTION REPEAT$
|
||||
|
||||
CLS
|
||||
LOCATE 10, 10
|
||||
PRINT "Do you want to enter or modify another module ?";
|
||||
|
||||
DO
|
||||
A$ = INKEY$
|
||||
LOOP WHILE A$ = ""
|
||||
|
||||
REPEAT$ = A$
|
||||
|
||||
END FUNCTION
|
||||
|
||||
SUB SAVEDATA (SEL%)
|
||||
|
||||
CLS
|
||||
LOCATE 10, 15
|
||||
PRINT "Saving record in file HVIN.DAT"
|
||||
|
||||
T = TIMER
|
||||
DO
|
||||
LOOP WHILE (TIMER - T) < 1
|
||||
|
||||
OPEN "T:\ENGR\ATE\HVU\HVDATA\HVIN.DAT" FOR RANDOM AS #1 LEN = LEN(SPECS)
|
||||
|
||||
IF SEL% = 0 THEN
|
||||
NR% = LOF(1) / LEN(SPECS)
|
||||
SEL% = NR% + 1
|
||||
END IF
|
||||
PUT #1, SEL%, SPECS
|
||||
CLOSE #1
|
||||
|
||||
END SUB
|
||||
|
||||
SUB SORTALL
|
||||
|
||||
'This sub does a bubble sort of the model numbers in the database
|
||||
'records FOR ALL MODELS
|
||||
|
||||
CONST FALSE = 0, TRUE = NOT FALSE
|
||||
|
||||
OPEN "T:\ENGR\ATE\HVU\HVDATA\HVIN.DAT" FOR RANDOM AS #1 LEN = LEN(SPECS)
|
||||
|
||||
NUMRECORD% = LOF(1) / LEN(SPECS)
|
||||
|
||||
PRINT
|
||||
PRINT TAB(10); "Sorting database, please wait."
|
||||
|
||||
'Sort according to string value
|
||||
DO
|
||||
Swaps% = FALSE
|
||||
FOR I% = 1 TO NUMRECORD%
|
||||
GET #1, I%, SPECS
|
||||
A% = STRINGVAL%(SPECS.MODNAME)
|
||||
GET #1, I% + 1, SORTDATA2
|
||||
B% = STRINGVAL%(SORTDATA2.MODNAME)
|
||||
'Do sort...
|
||||
IF A% > B% THEN
|
||||
PUT #1, I%, SORTDATA2
|
||||
PUT #1, (I% + 1), SPECS
|
||||
Swaps% = I%
|
||||
END IF
|
||||
NEXT I%
|
||||
LOOP WHILE Swaps%
|
||||
|
||||
CLOSE #1
|
||||
|
||||
END SUB
|
||||
|
||||
SUB SORTDB (ENDFLAG%)
|
||||
|
||||
'This sub does a bubble sort of the model numbers in the database
|
||||
'records FOR A SELECTED MODEL FAMILY
|
||||
|
||||
'CONST FALSE = 0, TRUE = NOT FALSE
|
||||
|
||||
'Create a file containing just the model numbers of all of the
|
||||
'records in the database for the model family selected
|
||||
|
||||
OPEN "T:\ENGR\ATE\HVU\HVDATA\HVSORT.DAT" FOR RANDOM AS #2 LEN = LEN(SORTDATA1)
|
||||
OPEN "T:\ENGR\ATE\HVU\HVDATA\HVIN.DAT" FOR RANDOM AS #1 LEN = LEN(SPECS)
|
||||
|
||||
NUMRECORD% = LOF(1) / LEN(SPECS)
|
||||
|
||||
SELFAM% = MENU3%
|
||||
IF SELFAM% = 4 THEN
|
||||
ENDFLAG% = 1
|
||||
CLOSE #1
|
||||
CLOSE #2
|
||||
CLOSE #3
|
||||
EXIT SUB 'exit selected from MENU3%
|
||||
END IF
|
||||
|
||||
N% = 0
|
||||
|
||||
FOR I% = 1 TO NUMRECORD%
|
||||
GET #1, I%, SPECS
|
||||
MN$ = SPECS.MODNAME
|
||||
SORTDATA1.MODNAME = MN$
|
||||
SORTDATA1.RECNUM = I%
|
||||
|
||||
SELECT CASE SELFAM% 'Pick models in selected family
|
||||
|
||||
CASE 1
|
||||
'SPECS.MODNAME = "SCM5Bxx-xxxx "
|
||||
IF LEFT$(MN$, 3) = "SCM" THEN
|
||||
PUT #2, , SORTDATA1 'write record # and model number
|
||||
N% = N% + 1 'found a record
|
||||
END IF
|
||||
CASE 2
|
||||
'SPECS.MODNAME = "DSCAxx-xxxx "
|
||||
IF LEFT$(MN$, 3) = "DSC" THEN
|
||||
PUT #2, , SORTDATA1 'write record # and model number
|
||||
N% = N% + 1 'found a record
|
||||
END IF
|
||||
|
||||
CASE 3
|
||||
'SPECS.MODNAME = "8Bxx-xxxx "
|
||||
IF LEFT$(MN$, 2) = "8B" THEN
|
||||
PUT #2, , SORTDATA1 'write record # and model number
|
||||
N% = N% + 1 'found a record
|
||||
END IF
|
||||
|
||||
|
||||
|
||||
END SELECT
|
||||
NEXT
|
||||
|
||||
SORTDATA1.MODNAME = "99999999-9999" 'always sorted to last value.
|
||||
SORTDATA1.RECNUM = -1 'flag indicating end of new data (over writes old)
|
||||
PUT #2, , SORTDATA1
|
||||
|
||||
CLOSE #1
|
||||
CLOSE #2
|
||||
CLOSE #3
|
||||
END SUB
|
||||
|
||||
FUNCTION STRINGVAL% (A$)
|
||||
|
||||
'Calculate and return the value of the characters in A$
|
||||
'which follow the hyphen
|
||||
|
||||
T% = LEN(A$)
|
||||
|
||||
DO
|
||||
L% = L% + 1
|
||||
LOOP UNTIL MID$(A$, L%, 1) = "-" OR L% = T%
|
||||
|
||||
STRINGVAL% = VAL(RIGHT$(A$, T% - L%))
|
||||
|
||||
END FUNCTION
|
||||
|
||||
@@ -0,0 +1,776 @@
|
||||
DECLARE SUB INSTALLDUT (NUMDUT%)
|
||||
'Library of functions used with SCM5B33 and DSCA33 test programs.
|
||||
'Created from LIBATE.BAS
|
||||
'Author: John Lehman
|
||||
'Date: 06/21/99
|
||||
'
|
||||
' REVISION RECORD
|
||||
'
|
||||
'Date of Change Description
|
||||
'-------------- -----------
|
||||
'06/21/99 JL Initial Release
|
||||
|
||||
DECLARE SUB CHANGEDN (SN$) 'Allow user to change dash number
|
||||
DECLARE SUB CONTINUE () 'Waits for a key press
|
||||
DECLARE SUB GETNEXTSN (TIME2!, SERNO$(), NUMDUT%) 'Allows user the change the SN info for a new group of modules
|
||||
DECLARE SUB GETSN (SN$) 'Gets DUT serial number from user
|
||||
DECLARE SUB INPUTSN (SERNO$(), NUMDUT%) 'Gathers the SN infor for the unit in slot 1
|
||||
DECLARE SUB HS1 () 'GPIB communications handshake
|
||||
DECLARE SUB pause (TIME!) 'Pause for TIME
|
||||
|
||||
DECLARE FUNCTION BESTFIT! (SLOPE!, OFFSET1!, INSIM!(), NUMPTS%, ERROROUT!(), L%)
|
||||
DECLARE FUNCTION REPEAT2$ (MN$, ELAP2!, TIME2!, SN$)
|
||||
'** Calculates besfit line and max error
|
||||
DECLARE FUNCTION KEYBDIN$ () '** Get keyboard input
|
||||
DECLARE FUNCTION REPEAT$ (MN$, ELAP2!, TIME2!) '** Ask if you would like to repeat test
|
||||
DECLARE FUNCTION READDVM! (CH%, FUNC$, RANGETXT$, RANGENUM!, RESOLTXT$, RESOLNUM!) '**
|
||||
DECLARE FUNCTION STRINGVAL% (A$) '**
|
||||
DECLARE FUNCTION UPSN$ (SN$, NUMDUT%) '** Increments dash# of serial#
|
||||
'** Function called from main programs.
|
||||
' Declare in programs which call this function.
|
||||
|
||||
'GPIB Communication routines for dataforth GPIB card
|
||||
DECLARE SUB HS2 ()
|
||||
DECLARE SUB HS1 ()
|
||||
DECLARE SUB INIT488 (DEVADDR%)
|
||||
DECLARE SUB IO488 (DDATA$, DEVADDR%, SEL4%)
|
||||
|
||||
'Kepco DPS 125-0.5M control routines via RS-232C
|
||||
DECLARE SUB INITPS (DPSADDR%, OVERI!, OVERV!) 'initializes supply
|
||||
DECLARE SUB SETDPS (DPSADDR%, VSUPPLY!) 'Sets Kepco DPS power supply
|
||||
DECLARE FUNCTION POWERIO$ (DPSADDR%, CMD$) '** Kepco DPS power supply I/O
|
||||
|
||||
'HP33120A (function generator) control routines via GPIB
|
||||
DECLARE SUB FUNGEN33120A (CMD$, VALUE!) 'Write command to func. gen.
|
||||
|
||||
'HP34970A (data acquisition/switch unit) control routines via GPIB
|
||||
DECLARE SUB DVMCONF (CH%, FUNC$, RANGETXT$, RANGENUM!, RESOLTXT$, RESOLNUM!) 'Configure DVM
|
||||
DECLARE SUB DVMSENS (CH%, FUNC1$, FUNC2$, SETTXT$, SETNUM!) 'Configure DVM
|
||||
DECLARE SUB SETDAC3 (DACNUM%, VOLTAGE!) 'Set 34907 DACs
|
||||
DECLARE SUB SETSWITCH (CH%, STATE%) 'Set switch on 34903A 20-ch actuator
|
||||
DECLARE FUNCTION GETREADING! () '** Send trigger & read dvm
|
||||
|
||||
'HP760A meter calibrator routines
|
||||
DECLARE SUB HP760APANEL (VALUE!, UNITS%) 'Draw a display of instrument panel.
|
||||
|
||||
COMMON SHARED PON%, IINSEN1.MEAS!, IOUTSEN1.MEAS!(), IOUTSEN2.MEAS!(), SERNO$(), SN$, PSCOM$, GENOUTMAX!, PCBNO$
|
||||
COMMON SHARED BADDRS%, DPSADDR%, PSPORT%, DASADDR%, GENADDR%, DIOCH01DATA%, DIOCH02DATA%
|
||||
|
||||
CONST MTA% = &H40 'GPIB talk address
|
||||
CONST MLA% = &H20 'GPIB listen address
|
||||
|
||||
'********************************************************************
|
||||
'Assign constants to Kepco DPS power supply commands:
|
||||
CONST PSVOLT$ = "STV=" 'Set terminal voltage
|
||||
CONST SUPPLYON$ = "SOP=ON" 'Set output to ON
|
||||
CONST ISTAT$ = "RCS" 'Read Current protection status
|
||||
CONST SETIMAX$ = "SOC=" 'Set overcurent limit...4mA resol.
|
||||
|
||||
'********************************************************************
|
||||
'Assign constants to HP freq. gen. commands
|
||||
CONST FREQ$ = "FREQ"
|
||||
|
||||
'********************************************************************
|
||||
'Assign constants to HP DAS commands
|
||||
CONST SETDAC$ = "SOUR:VOLT"
|
||||
|
||||
'********************************************************************
|
||||
'Assign constants to HP DAS configuration
|
||||
CONST DAC1.CH% = 304 'DAC #1
|
||||
CONST DAC2.CH% = 305 'DAC #2
|
||||
|
||||
KEY(10) ON 'Activates F10 key
|
||||
ON KEY(10) GOSUB FINISH 'Traps for F10 key Exits if pressed
|
||||
|
||||
FINISH: END
|
||||
|
||||
FUNCTION BESTFIT! (SLOPE!, OFFSET1!, INSIM!(), NUMPTS%, ERROROUT!(), L%)
|
||||
|
||||
'Calculates Max error of data from bestfit line
|
||||
|
||||
MTERR! = 0
|
||||
MIN! = INSIM!(L%, 1)
|
||||
MAX! = INSIM!(L%, NUMPTS%)
|
||||
|
||||
FOR INC% = 1 TO NUMPTS% 'Increments thru test points
|
||||
|
||||
BVEC! = INSIM!(L%, INC%) * SLOPE! + OFFSET1! 'Calculates point on best fit line
|
||||
AVEC! = ERROROUT!(L%, INC%) 'Gets corresponding data point
|
||||
|
||||
TERR! = AVEC! - BVEC! 'Calculates difference
|
||||
|
||||
IF ABS(TERR!) > ABS(MTERR!) THEN 'Checks if bigger than last Max
|
||||
MTERR! = TERR! 'Assigns max error
|
||||
END IF
|
||||
NEXT
|
||||
|
||||
BESTFIT! = MTERR! 'Passes Max error back
|
||||
|
||||
END FUNCTION
|
||||
|
||||
SUB CHANGEDN (SN$)
|
||||
|
||||
N = 1
|
||||
NWO$ = ""
|
||||
DO
|
||||
C$ = MID$(SN$, N, 1)
|
||||
NWO$ = NWO$ + C$
|
||||
N = N + 1
|
||||
LOOP WHILE C$ <> "-"
|
||||
WO$ = LEFT$(SN$, N)
|
||||
PRINT "Current serial number is: "; SN$
|
||||
INPUT "Enter the new dash number (Press 'Enter' for 1) "; DS$
|
||||
IF DS$ = "" THEN DS$ = "1"
|
||||
SN$ = NWO$ + DS$
|
||||
|
||||
END SUB
|
||||
|
||||
SUB DVMCONF (CH%, FUNC$, RANGETXT$, RANGENUM!, RESOLTXT$, RESOLNUM!)
|
||||
|
||||
' Inputs:
|
||||
' CH% = channel to be measured
|
||||
' FUNC$ = parameter to be measured
|
||||
' RANGETXT$ = measurement range, specified in text
|
||||
' If 'N/A', don't include in command. Not needed for FUNC$
|
||||
' If "", range specified in RANGENUM! or not specified
|
||||
' RANGENUM! = measurement range, specified numerically
|
||||
' If 0, range specified in RANGETXT$ or not specified
|
||||
' RESOLTXT$ = indicate how resolution is specified. 0 = text, 1 = numerically
|
||||
' If 'N/A', don't include in command. Not needed for FUNC$
|
||||
' IF "", resolution specified in RESOLNUM! or not specified
|
||||
' RESOLNUM! = measurement resolution
|
||||
' 0 = Resolution specified in RESOLTXT$ or not specified
|
||||
' Outputs: None
|
||||
' Description - configures HP34970A to take a measurement
|
||||
'
|
||||
' Reference;
|
||||
' MEAS and CONF use the following default instrument settings
|
||||
' Integration Time 1PLC
|
||||
' Input Resistane 10Mohm, fixed for all DCV ranges
|
||||
' AC Filter 20Hz (medium)
|
||||
' Scan List Redefined when command executed
|
||||
' Scan Interval Source Immediate
|
||||
' Scan Count 1 Scan Sweep
|
||||
' Channel Delay Automatic Delay
|
||||
|
||||
IF RANGETXT$ = "N/A" THEN 'String command only.
|
||||
'No range or resolution data.
|
||||
DDATA$ = "CONF:" + FUNC$ + " (@" + STR$(CH%) + ")"
|
||||
ELSE
|
||||
IF RANGENUM! <> 0 THEN 'Range specified numerically
|
||||
RG$ = STR$(RANGENUM!)
|
||||
ELSE 'Range specified w/ text
|
||||
RG$ = " " + RANGETXT$
|
||||
END IF
|
||||
IF RESOLNUM! <> 0 THEN 'Resolution specified numerically
|
||||
RES$ = STR$(RESOLNUM!)
|
||||
ELSE 'Resolution specified w/ text
|
||||
IF RESOLTXT$ <> "" THEN
|
||||
RES$ = RESOLTXT$
|
||||
ELSE
|
||||
RES$ = ""
|
||||
END IF
|
||||
END IF
|
||||
DDATA$ = "CONF:" + FUNC$ + RG$ + RES$ + ",(@" + STR$(CH%) + ")"
|
||||
END IF
|
||||
|
||||
'Y% = CSRLIN
|
||||
'LOCATE 22
|
||||
'PRINT "DVMCONF a" + DDATA$ + "b";
|
||||
'PRINT SPC(10); : PRINT
|
||||
'LOCATE Y%
|
||||
|
||||
CALL IO488(DDATA$, DASADDR%, 1) 'Write configuration
|
||||
|
||||
END SUB
|
||||
|
||||
SUB DVMSENS (CH%, FUNC1$, FUNC2$, SETTXT$, SETNUM!)
|
||||
|
||||
' Inputs:
|
||||
' CH% = channel to be measured
|
||||
' FUNC1$ = parameter to be measured
|
||||
' FUNC2$ = parameter to be configured
|
||||
' SETTXT$ = configuration, specified in text
|
||||
' If "", specified in SETNUM!
|
||||
' SETNUM! = configuration, specified numerically
|
||||
' If 0, range specified in SETTXT$
|
||||
' Outputs: None
|
||||
' Description - configures HP34970A to take a measurement
|
||||
'
|
||||
' Reference;
|
||||
' DC Voltage and Current Readings
|
||||
' Integration Time Resolution Digits Bits
|
||||
' 0.02PLC <0.0001 * Range 4 1/2 15
|
||||
' 0.2PLC <0.00001 * Range 5 1/2 18
|
||||
' 1PLC <0.000003 * Range 5 1/2 20
|
||||
' 2PLC <0.0000022 * Range 6 1/2 21
|
||||
' 10PLC <0.000001 * Range 6 1/2 24
|
||||
' 20PLC <0.0000008 * Range 6 1/2 25
|
||||
' 100PLC <0.0000003 * Range 6 1/2 26
|
||||
' 200PLC <0.00000022 * Range 6 1/2 26
|
||||
'
|
||||
' AC Voltage and Current Readings
|
||||
' Filter Readings/s Settling Delay Digits
|
||||
' 3Hz 0.14 1.5s 6 1/2
|
||||
' 20Hz 1 0.2s 6 1/2
|
||||
' 200Hz 8 0.02s 6 1/2
|
||||
|
||||
IF SETNUM! <> 0 THEN 'specified numerically
|
||||
ST$ = STR$(SETNUM!)
|
||||
ELSE 'specified w/ text
|
||||
ST$ = " " + SETTXT$
|
||||
END IF
|
||||
DDATA$ = "SENS:" + FUNC1$ + ":" + FUNC2$ + ST$ + ",(@" + STR$(CH%) + ")"
|
||||
|
||||
'Y% = CSRLIN
|
||||
'LOCATE 23
|
||||
'PRINT "DVMSENSE a" + DDATA$ + "b";
|
||||
'PRINT SPC(10); : PRINT
|
||||
'LOCATE Y%
|
||||
|
||||
CALL IO488(DDATA$, DASADDR%, 1) 'Write configuration
|
||||
|
||||
END SUB
|
||||
|
||||
SUB FUNGEN33120A (CMD$, VALUE!)
|
||||
|
||||
' Inputs - String of commands to be sent to the HP33120A
|
||||
' Outputs - None
|
||||
' Description - Writes a string of commands to the HP33120A
|
||||
' function generator, through the IEEE 488 interface
|
||||
' If VALUE! = -99 then the command is a string only
|
||||
|
||||
IF VALUE! = -99 THEN
|
||||
VALUE2$ = "" 'Command contains no numerical value
|
||||
ELSEIF CMD$ = FREQ$ AND ABS(VALUE!) < .0001 THEN
|
||||
VALUE! = .0001 'Func. gen. min output = 100uHz
|
||||
VALUE2$ = STR$(VALUE!)
|
||||
ELSEIF (LEFT$(CMD$, 3) = "FSK" OR LEFT$(CMD$, 7) = "FREQ:ST") AND VALUE! <= .0001 THEN
|
||||
VALUE! = .01 'Func. gen. min FSK value = 10mHz
|
||||
VALUE2$ = STR$(VALUE!) 'Func. gen. min sweep value = 10mHz
|
||||
ELSE
|
||||
VALUE2$ = STR$(VALUE!)
|
||||
END IF
|
||||
|
||||
DDATA$ = CMD$ + " " + VALUE2$
|
||||
|
||||
'Y% = CSRLIN
|
||||
'LOCATE 20
|
||||
'PRINT "FUNGEN33120A a" + DDATA$ + "b";
|
||||
'PRINT SPC(10); : PRINT
|
||||
'LOCATE Y%
|
||||
|
||||
CALL IO488(DDATA$, GENADDR%, 1) 'Write data to generator
|
||||
|
||||
END SUB
|
||||
|
||||
FUNCTION GETREADING!
|
||||
|
||||
' Inputs: None
|
||||
' Outputs: None
|
||||
' Description - configures HP34970A trigger source, sends a trigger
|
||||
' command, reads the DVM and returns the reading.
|
||||
|
||||
DDATA$ = "TRIG:SOUR BUS" 'Trigger source = bus
|
||||
CALL IO488(DDATA$, DASADDR%, 1)
|
||||
|
||||
DDATA$ = "INIT" 'set 'wait-for-trigger' state
|
||||
CALL IO488(DDATA$, DASADDR%, 1)
|
||||
|
||||
DDATA$ = "*TRG" 'send trigger
|
||||
CALL IO488(DDATA$, DASADDR%, 1)
|
||||
|
||||
DDATA$ = "FETC?" 'retrieve reading
|
||||
CALL IO488(DDATA$, DASADDR%, 1)
|
||||
CALL IO488(DDATA$, DASADDR%, 2)
|
||||
GETREADING! = VAL(DDATA$)
|
||||
|
||||
END FUNCTION
|
||||
|
||||
SUB HP760APANEL (VALUE!, UNITS%)
|
||||
|
||||
'Print a display of the HP760A front panel to show number setting.
|
||||
'Inputs; VALUE! = Value of voltage or current to be set
|
||||
' UNITS% 1 = Voltage
|
||||
' 2 = Current
|
||||
' 3 = None, FUNCTION = STD BY
|
||||
|
||||
' 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75
|
||||
'
|
||||
DIM DIG%(7) 'Display digits
|
||||
TOPLOC% = 16
|
||||
|
||||
IF UNITS% = 1 THEN
|
||||
SCALE! = 1 'Max voltage output is 1000V
|
||||
LEDTAB% = 34
|
||||
ELSEIF UNITS% = 2 THEN
|
||||
SCALE! = 100 'Max current output is 10A
|
||||
LEDTAB% = 14
|
||||
ELSE
|
||||
LEDTAB% = 0
|
||||
END IF
|
||||
|
||||
'Set meter for twice max D.U.T. input
|
||||
'Set only one digit. Setting other digits
|
||||
'after most significant digit does not
|
||||
'always increase output signal magnitude.
|
||||
IF VALUE! * 2 >= 100 THEN
|
||||
SETVALUE! = CINT(VALUE! * 2 / 100) * 100 * SCALE!
|
||||
ELSEIF VALUE! * 2 >= 10 THEN
|
||||
SETVALUE! = CINT(VALUE! * 2 / 10) * 10 * SCALE!
|
||||
ELSEIF VALUE! * 2 >= 1 THEN
|
||||
SETVALUE! = CINT(VALUE! * 2) * SCALE!
|
||||
ELSEIF VALUE! * 2 >= .1 THEN
|
||||
SETVALUE! = CINT(VALUE! * 2 * 10) / 10 * SCALE!
|
||||
ELSEIF VALUE! * 2 >= .01 THEN
|
||||
SETVALUE! = CINT(VALUE! * 2 * 100) / 100 * SCALE!
|
||||
ELSEIF VALUE! * 2 >= .001 THEN
|
||||
SETVALUE! = CINT(VALUE! * 2 * 1000) / 1000 * SCALE!
|
||||
ELSEIF VALUE! * 2 >= .0001 THEN
|
||||
SETVALUE! = CINT(VALUE! * 2 * 10000) / 10000 * SCALE!
|
||||
END IF
|
||||
|
||||
NUM$ = STR$(SETVALUE!)
|
||||
|
||||
FOR L% = 1 TO 7
|
||||
DIG%(L%) = 0
|
||||
NEXT
|
||||
|
||||
IF SETVALUE! >= 1000 THEN
|
||||
DIG%(7) = 9
|
||||
ELSEIF SETVALUE! >= 100 THEN
|
||||
DIG%(7) = VAL(MID$(NUM$, 2, 1))
|
||||
ELSEIF SETVALUE! >= 10 THEN
|
||||
DIG%(6) = VAL(MID$(NUM$, 2, 1))
|
||||
ELSEIF SETVALUE! >= 1 THEN
|
||||
DIG%(5) = VAL(MID$(NUM$, 2, 1))
|
||||
ELSEIF SETVALUE! >= .1 THEN
|
||||
DIG%(4) = VAL(MID$(NUM$, 3, 1))
|
||||
ELSEIF SETVALUE! >= .01 THEN
|
||||
DIG%(3) = VAL(MID$(NUM$, 4, 1))
|
||||
ELSEIF SETVALUE! >= .001 THEN
|
||||
DIG%(3) = VAL(MID$(NUM$, 5, 1))
|
||||
ELSEIF SETVALUE! >= .0001 THEN
|
||||
DIG%(3) = VAL(MID$(NUM$, 6, 1))
|
||||
END IF
|
||||
|
||||
FOR L% = 1 TO 7
|
||||
|
||||
LOCATE TOPLOC%, (L% - 1) * 10 + 7 'top left corner
|
||||
PRINT CHR$(201)
|
||||
|
||||
FOR M% = 8 TO 10 'top horizontal line
|
||||
LOCATE TOPLOC%, (L% - 1) * 10 + M%
|
||||
PRINT CHR$(205)
|
||||
NEXT M%
|
||||
|
||||
LOCATE TOPLOC%, (L% - 1) * 10 + 11 'top right corner
|
||||
PRINT CHR$(187)
|
||||
|
||||
FOR N% = TOPLOC% + 1 TO TOPLOC% + 3
|
||||
LOCATE N%, (L% - 1) * 10 + 7 'left vertical line
|
||||
PRINT CHR$(186)
|
||||
IF N% = TOPLOC% + 2 THEN 'print display digit
|
||||
LOCATE N%, ((L% - 1) * 10 + 8)
|
||||
PRINT DIG%(8 - L%)
|
||||
END IF
|
||||
LOCATE N%, (L% - 1) * 10 + 11 'right vertical line
|
||||
PRINT CHR$(186)
|
||||
NEXT N%
|
||||
|
||||
LOCATE TOPLOC% + 4, (L% - 1) * 10 + 7 'bottom left corner
|
||||
PRINT CHR$(200)
|
||||
|
||||
FOR M% = 8 TO 10 'bottom horizontal line
|
||||
LOCATE TOPLOC% + 4, (L% - 1) * 10 + M%
|
||||
PRINT CHR$(205)
|
||||
NEXT M%
|
||||
|
||||
LOCATE TOPLOC% + 4, (L% - 1) * 10 + 11 'bottom right corner
|
||||
PRINT CHR$(188)
|
||||
|
||||
NEXT L%
|
||||
|
||||
IF LEDTAB% <> 0 THEN
|
||||
LOCATE TOPLOC% + 2, LEDTAB% 'print LED location
|
||||
PRINT CHR$(15)
|
||||
END IF
|
||||
|
||||
END SUB
|
||||
|
||||
SUB HS1
|
||||
|
||||
' Module Name - HS1
|
||||
'
|
||||
' Inputs - None
|
||||
' Outputs - None
|
||||
' Description - HS1 waits for EOI to occur with ATN = 0
|
||||
' >>>> Check Bit #4 for true (EOI)
|
||||
|
||||
BYTE% = 0
|
||||
WHILE (BYTE% AND 16) = 0
|
||||
BYTE% = INP(BADDRS% + 0)
|
||||
WEND
|
||||
|
||||
END SUB
|
||||
|
||||
SUB HS2
|
||||
|
||||
' Module Name - HS2
|
||||
'
|
||||
' Inputs - None
|
||||
' Outputs - None
|
||||
' Description - HS1 checks that serial poll active state
|
||||
' has occured with rsv set in the serial poll register.
|
||||
' >>>>> Check Bit #5 for true (SPAS)
|
||||
|
||||
BYTE% = 0
|
||||
WHILE NOT BYTE% AND 32 AND BYTE% <> 40
|
||||
BYTE% = INP(BADDRS% + 0)
|
||||
WEND
|
||||
|
||||
END SUB
|
||||
|
||||
SUB INIT488 (DEVADDR%)
|
||||
|
||||
' Module Name - INIT488
|
||||
'
|
||||
' Inputs - None
|
||||
' Outputs - None
|
||||
' Description - Initalizes the IEEE 488 interface
|
||||
|
||||
OUT (BADDRS% + 8), &H0 'Configure PC4311 as controller
|
||||
OUT (BADDRS% + 3), &H80 'Release ACDS holdoff, set operation
|
||||
OUT (BADDRS% + 3), &H0 'Software reset, set operation
|
||||
OUT (BADDRS% + 4), DEVADDR% 'Enable dual primary addressing mode
|
||||
OUT (BADDRS% + 0), &H0 'Disable all interrupts
|
||||
OUT (BADDRS% + 3), &H8F 'Request control, clear operation
|
||||
OUT (BADDRS% + 3), &HF 'Send remote enable, clear operation
|
||||
OUT (BADDRS% + 3), &H90 'Listen only, set operation
|
||||
|
||||
END SUB
|
||||
|
||||
SUB INITPS (DPSADDR%, OVERI!, OVERV!)
|
||||
|
||||
'Initialize Kepco power supply
|
||||
PS$ = POWERIO$(DPSADDR%, "ZER") 'Clear errors if any
|
||||
PS$ = POWERIO$(DPSADDR%, "SMD=OC") 'Select over-current protection mode
|
||||
'Set over-current limit
|
||||
STLEN% = LEN(STR$(OVERI!))
|
||||
PS$ = POWERIO$(DPSADDR%, SETIMAX$ + RIGHT$(STR$(OVERI!), STLEN% - 1))
|
||||
STLEN% = LEN(STR$(OVERV!))
|
||||
PS$ = POWERIO$(DPSADDR%, "SOV=" + RIGHT$(STR$(OVERV!), STLEN% - 1))'Set over-voltage limit
|
||||
|
||||
END SUB
|
||||
|
||||
SUB IO488 (DDATA$, DEVADDR%, SEL4%)
|
||||
'***********************************************************************************************
|
||||
'* *
|
||||
'* Description: Output and input to GPIB device using PC4311 GPIB card *
|
||||
'* *
|
||||
'* Input : DDATA$ -- Data string to GPIB device *
|
||||
'* DEVADDR% -- Device address desired *
|
||||
'* SEL4% -- Write (1) or Read (2) command *
|
||||
'* *
|
||||
'* Output : DDATA$ -- Data string from GPIB device *
|
||||
'* *
|
||||
'***********************************************************************************************
|
||||
CALL INIT488(DEVADDR%) 'Address GPIB card
|
||||
|
||||
SELECT CASE SEL4%
|
||||
'Write...
|
||||
CASE 1
|
||||
BYTE% = MLA% OR DEVADDR% 'GPIB device address
|
||||
OUT (BADDRS% + 7), BYTE% 'Send out to GPIB card
|
||||
CALL HS1 'Wait for recieved status
|
||||
OUT (BADDRS% + 3), &H8A 'Request control, clear operation
|
||||
OUT (BADDRS% + 3), &HB 'Remote enable, clear
|
||||
FOR X1 = 1 TO LEN(DDATA$) 'Create character and send out
|
||||
BYTE% = ASC(MID$(DDATA$, X1, 1))
|
||||
OUT (BADDRS% + 7), BYTE%
|
||||
CALL HS1
|
||||
NEXT
|
||||
OUT (BADDRS% + 7), &HD 'Send CR, tell device to execute command
|
||||
CALL HS1 'Wait for recieved status
|
||||
OUT (BADDRS% + 7), &HA 'Send LF
|
||||
CALL HS1 'Wait for recieved status
|
||||
OUT (BADDRS% + 3), &HC 'Remote enable, set operation
|
||||
OUT (BADDRS% + 3), &HA 'Clear talk only
|
||||
OUT (BADDRS% + 7), &H3F 'Un-listen
|
||||
'Read
|
||||
CASE 2
|
||||
DDATA$ = ""
|
||||
BYTE% = MTA% OR DEVADDR% 'GPIB device address
|
||||
OUT (BADDRS% + 7), BYTE% 'Send out to GPIB card
|
||||
CALL HS1 'Wait for recieved status
|
||||
OUT (BADDRS% + 3), &H89 'Request control, clear
|
||||
OUT (BADDRS% + 3), &HB 'Remote enable, clear
|
||||
DO
|
||||
CALL HS2
|
||||
DATABYTE = INP(BADDRS% + 7)
|
||||
IF DATABYTE <> &HA THEN
|
||||
DDATA$ = DDATA$ + CHR$(DATABYTE)
|
||||
END IF
|
||||
LOOP WHILE DATABYTE <> &HA
|
||||
OUT (BADDRS% + 3), &HC 'Remote enable, set command
|
||||
OUT (BADDRS% + 3), &H9 'Clear listen only
|
||||
OUT (BADDRS% + 7), &H5F 'Un-talk command
|
||||
END SELECT
|
||||
CALL HS1 'Wait for recieved status
|
||||
|
||||
END SUB
|
||||
|
||||
FUNCTION KEYBDIN$
|
||||
|
||||
'Get input from keyboard
|
||||
DO
|
||||
X$ = INKEY$
|
||||
LOOP WHILE X$ = ""
|
||||
KEYBDIN$ = UCASE$(X$)
|
||||
|
||||
END FUNCTION
|
||||
|
||||
SUB pause (TIME!)
|
||||
|
||||
T1! = TIMER
|
||||
DO
|
||||
T2! = TIMER
|
||||
LOOP UNTIL (T2! - T1!) > TIME!
|
||||
|
||||
END SUB
|
||||
|
||||
FUNCTION POWERIO$ (DPSADDR%, CMD$)
|
||||
|
||||
'This function sends commands and receives output from the
|
||||
'Kepco DPS 125-0.5M programmable power supply.
|
||||
'Inputs: power supply address, command
|
||||
'Outputs: supply response, if any
|
||||
|
||||
'Open and initialize the power supply controller communications channel for I/O
|
||||
PSCOM$ = "COM" + RIGHT$(STR$(PSPORT%), 1) + ":9600,N,8,1"
|
||||
OPEN PSCOM$ FOR RANDOM AS #1
|
||||
|
||||
DEVSEL = DPSADDR% + &HE0
|
||||
RXOK = DPSADDR% + &HC0
|
||||
|
||||
'DEVSEL must be sent without a <CR>
|
||||
'prior to each command
|
||||
'Send DEVSEL byte to DPS
|
||||
PRINT #1, CHR$(DEVSEL);
|
||||
|
||||
N = 0
|
||||
'Await input buffer to become non-zero
|
||||
'print message if not successful
|
||||
|
||||
DO
|
||||
IF N > 5000 THEN
|
||||
PRINT "COM ERROR - INPUT BUFFER EMPTY"
|
||||
CLOSE #1 'close power supply communication channel
|
||||
END 'exit program
|
||||
END IF
|
||||
N = N + 1
|
||||
LOOP WHILE (LOC(1) = 0)
|
||||
|
||||
'RXOK is returned (without a <CR>) in
|
||||
'response to DEVSEL
|
||||
A$ = INPUT$(1, #1) 'Input RXOK byte
|
||||
|
||||
PRINT #1, CMD$
|
||||
'Await input buffer to become non-zero
|
||||
DO
|
||||
LOOP WHILE (LOC(1) = 0)
|
||||
|
||||
A$ = INPUT$(1, #1) 'Input first (non-ASCII) character of
|
||||
'string and discard
|
||||
|
||||
INPUT #1, B$ 'Input balance of string
|
||||
POWERIO$ = B$
|
||||
CLOSE #1 'close power supply communication channel
|
||||
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION READDVM! (CH%, FUNC$, RANGETXT$, RANGENUM!, RESOLTXT$, RESOLNUM!)
|
||||
|
||||
' Inputs:
|
||||
' CH% = channel to be measured
|
||||
' FUNC$ = parameter to be measured
|
||||
' RANGETXT$ = indicate how range is specified. 0 = text, 1 = numerically
|
||||
' If "", range specified in RANGENUM! or not specified
|
||||
' RANGENUM! = measurement range
|
||||
' If 0, range specified in RANGETXT$ or not specified
|
||||
' RESOLTXT$ = indicate how resolution is specified. 0 = text, 1 = numerically
|
||||
' IF "", resolution specified in RESOLNUM! or not specified
|
||||
' RESOLNUM! = measurement resolution
|
||||
' 0 = Resolution specified in RESOLTXT$ or not specified
|
||||
' Outputs: None
|
||||
' Description - configures HP34970A to take a measurement, reads
|
||||
' the DVM and returns the reading.
|
||||
'
|
||||
' Reference;
|
||||
' MEAS and CONF use the following default instrument settings
|
||||
' Integration Time 1PLC (5 1/2 digits, 20 bits)
|
||||
' Input Resistane 10Mohm, fixed for all DCV ranges
|
||||
' AC Filter 20Hz (medium)
|
||||
' Scan List Redefined when command executed
|
||||
' Scan Interval Source Immediate
|
||||
' Scan Count 1 Scan Sweep
|
||||
' Channel Delay Automatic Delay
|
||||
'
|
||||
' DVM Accuracy;
|
||||
' The following specs use the 3Hz filter
|
||||
' ACV +/-0.06% reading +/-0.04% range, 10Hz-20KHz, 0.1V - 100V, 1 year
|
||||
' ACV +/-0.06% reading +/-0.04% range, 10Hz-20KHz, 0.1V - 100V, 1 year
|
||||
' ACV +/-0.05% reading +/-0.08% range, 10Hz-20KHz, 300V, 90 day
|
||||
' ACV +/-0.05% reading +/-0.08% range, 10Hz-20KHz, 300V, 90 day
|
||||
'
|
||||
' If the 20Hz filter is used, add the following low frequency error
|
||||
' +/-0.06% reading, 40Hz-100Hz
|
||||
' +/-0.01% reading, 100Hz-200Hz
|
||||
' +/-0.0% reading, 200Hz->1000Hz
|
||||
|
||||
IF RANGENUM! <> 0 THEN 'Range specified numerically
|
||||
RG$ = STR$(RANGENUM!) + ","
|
||||
ELSE 'Range specified w/ text
|
||||
RG$ = RANGETXT$ + ","
|
||||
END IF
|
||||
IF RESOLNUM! <> 0 THEN 'Resolution specified numerically
|
||||
RES$ = STR$(RESOLNUM!) + ","
|
||||
ELSE 'Resolution specified w/ text
|
||||
IF RESOLTXT$ <> "" THEN
|
||||
RES$ = RESOLTXT$ + ","
|
||||
ELSE
|
||||
RES$ = ""
|
||||
END IF
|
||||
END IF
|
||||
|
||||
DDATA$ = "MEAS:" + FUNC$ + "?" + RG$ + RES$ + "(@" + STR$(CH%) + ")"
|
||||
|
||||
'Y% = CSRLIN
|
||||
'LOCATE 21
|
||||
'PRINT "READDVM a" + DDATA$ + "b";
|
||||
'PRINT SPC(10); : PRINT
|
||||
'LOCATE Y%
|
||||
|
||||
CALL IO488(DDATA$, DASADDR%, 1) 'Write configuration
|
||||
CALL IO488(DDATA$, DASADDR%, 2) 'Read result
|
||||
|
||||
READDVM! = VAL(DDATA$)
|
||||
|
||||
END FUNCTION
|
||||
|
||||
FUNCTION REPEAT$ (MN$, ELAP2!, TIME2!)
|
||||
|
||||
CLS
|
||||
LOCATE 10, 10
|
||||
PRINT "Do you want to test another set of "; MN$; " modules?"
|
||||
|
||||
LOCATE 23, 49
|
||||
PRINT USING "Test Time: ## Min. ## Sec."; INT(ELAP2! / 60); ELAP2! - INT(ELAP2! / 60) * 60
|
||||
|
||||
DO
|
||||
A$ = INKEY$
|
||||
LOOP WHILE A$ = ""
|
||||
|
||||
IF UCASE$(A$) <> "N" THEN
|
||||
LOCATE 12, 10
|
||||
PRINT "Insert the next set of modules, starting with channel 1."
|
||||
CALL CONTINUE
|
||||
END IF
|
||||
|
||||
TIME2! = TIMER
|
||||
REPEAT$ = UCASE$(A$)
|
||||
|
||||
END FUNCTION
|
||||
|
||||
SUB SETDAC3 (DACNUM%, VOLTAGE!)
|
||||
|
||||
'Sets the HP34907 DACs
|
||||
'Both DACs are;
|
||||
' +/-12V out
|
||||
' 10mA max
|
||||
' 1mV resolution
|
||||
' ground referenced, cannot float
|
||||
'
|
||||
'DAC1 is on CH04, DAC2 is on CH05
|
||||
'Inputs; DACNUM% 1 = DAC1, 2 = DAC2
|
||||
' VOLTAGE! Voltage to set
|
||||
|
||||
SELECT CASE DACNUM%
|
||||
CASE 1
|
||||
CH% = DAC1.CH%
|
||||
CASE 2
|
||||
CH% = DAC2.CH%
|
||||
END SELECT
|
||||
|
||||
DDATA$ = SETDAC$ + " " + STR$(VOLTAGE!) + ",(@" + STR$(CH%) + ")"
|
||||
|
||||
'Y% = CSRLIN
|
||||
'LOCATE 21
|
||||
'PRINT "SETDAC3 a" + DDATA$ + "b";
|
||||
'PRINT SPC(10); : PRINT
|
||||
'LOCATE Y%
|
||||
|
||||
CALL IO488(DDATA$, DASADDR%, 1) 'Write configuration
|
||||
|
||||
END SUB
|
||||
|
||||
SUB SETDPS (DPSADDR%, VSUPPLY!)
|
||||
|
||||
'Set supply voltage
|
||||
SETP$ = POWERIO$(DPSADDR%, PSVOLT$ + RIGHT$(STR$(VSUPPLY!), LEN(STR$(VSUPPLY!)) - 1))
|
||||
SETP$ = POWERIO$(DPSADDR%, SUPPLYON$) 'enable power supply output
|
||||
CALL pause(.02) '20ms over-current prot. delay
|
||||
|
||||
IF POWERIO$(DPSADDR%, ISTAT$) = "RCS=01" THEN 'Check for over-current
|
||||
SOUND 1000, .5
|
||||
PRINT
|
||||
PRINT TAB(10); "Module supply current has exceeded maximum value."
|
||||
PRINT TAB(10); "Power supply has been disabled."
|
||||
CALL pause(1)
|
||||
'PRINT TAB(10); "Press any key to continue"
|
||||
'A$ = KEYBDIN$
|
||||
END IF
|
||||
|
||||
END SUB
|
||||
|
||||
SUB SETSWITCH (CH%, STATE%)
|
||||
|
||||
' Inputs:
|
||||
' CH% = channel to be closed
|
||||
' STATE% = switch state, 1 = CLOSED, 0 = OPEN
|
||||
' Outputs: None
|
||||
' Description - sets the specified switch on the HP34903A 20-channel
|
||||
' actuator
|
||||
|
||||
IF STATE% = 1 THEN 'close switch
|
||||
DDATA$ = "ROUT:CLOS " + "(@" + STR$(CH%) + ")"
|
||||
ELSE 'String command only
|
||||
DDATA$ = "ROUT:OPEN " + "(@" + STR$(CH%) + ")"
|
||||
END IF
|
||||
|
||||
'Y% = CSRLIN
|
||||
'LOCATE 22
|
||||
'PRINT "SETSWITCH a" + DDATA$ + "b";
|
||||
'PRINT SPC(10); : PRINT
|
||||
'LOCATE Y%
|
||||
|
||||
CALL IO488(DDATA$, DASADDR%, 1) 'Write configuration
|
||||
|
||||
END SUB
|
||||
|
||||
FUNCTION UPSN$ (SN$, NUMDUT%)
|
||||
|
||||
'SN$ = NNNN-DD
|
||||
|
||||
N% = 1
|
||||
WHILE MID$(SN$, N%, 1) <> "-"
|
||||
' PRINT MID$(SN$, N%, 1)
|
||||
SNTEMP$ = SNTEMP$ + MID$(SN$, N%, 1)
|
||||
N% = N% + 1
|
||||
WEND
|
||||
|
||||
L% = LEN(SN$)
|
||||
UPSN$ = SNTEMP$ + "-" + LTRIM$(STR$(VAL(MID$(SN$, N% + 1, L% - N%)) + NUMDUT%))
|
||||
|
||||
END FUNCTION
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
High Input Voltage Units test program directory containing the archived and
|
||||
release versions of the test and database programs.
|
||||
|
||||
Directory structure:
|
||||
|
||||
HVDATA: Current version of the high input voltage database file ("hvin.dat") is in this directory, while the archived previous versions are under the "History" subdirectory ("...\HVU\HVDATA\History").
|
||||
|
||||
|
||||
Release: The current released test program source files and executable are in this directory, while the archived previous versions are under the "History" subdirectory ("...\HVU\Release\History").
|
||||
|
||||
DBpgm: The current released high input voltage database modification program source files and executable are in this directory, while the archived previous versions are under the "History" subdirectory ("...\HVU\DBpgm\History").
|
||||
|
||||
|
||||
NOTE: The ongoing structure and naming convention for the archived ("history") files should include ALL of the source files (including the ".MAK" file) and the executable file for that version of the program, which should be located in a folder named for the date of release in this format: "year-month-date" with a four-digit year, two digit month (leading zero if required), and a two-digit date (leading zero if required). For example, "2014-07-31" for July 31, 2014. Maintaining this format will lead to consistent sorting when attempting to locate versions in the future. This folder should be created under the appropriate "History" subfolder (under "...\HVU\DBpgm\History" for database programs, ...\HVU\Release\History" for test programs, or "...\HVU\HVDATA\History" for database files.
|
||||
|
||||
FURTHER NOTE: Previously archived test and/or database programs do not necessarily follow the structure described above, and are all located under the "...\HVU\Released\History\_Old (Pre 2014-07-31)" folder.
|
||||
|
||||
Program release instructions:
|
||||
-----------------------------
|
||||
The updated test program executable (.EXE) file is copied to:
|
||||
"T:\ENGR\ATE\ProdSW\ATE"
|
||||
Then the batch file "TtoK.bat" is run (double-click on the file name in Window Explorer) in folder:
|
||||
"T:\ENGR\ATE\ProdSW\ATE Software Transfer Programs"
|
||||
|
||||
|
||||
Database release instructions:
|
||||
-----------------------------
|
||||
The updated test database (.DAT) file is copied to:
|
||||
"T:\ENGR\ATE\ProdSW\ATE\HVDATA"
|
||||
Then the batch file "TtoK.bat" is run (double-click on the file name in Window Explorer) in folder:
|
||||
"T:\ENGR\ATE\ProdSW\ATE Software Transfer Programs"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
TESTHV3.BAS
|
||||
TESTHV4.BAS
|
||||
NLIBATE3.BAS
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,47 @@
|
||||
"""SSH helper for AD2 access. Usage: python ssh_ad2.py <cmd>"""
|
||||
import subprocess, sys
|
||||
import paramiko, yaml
|
||||
|
||||
HOST = '192.168.0.6'
|
||||
USER = 'sysadmin'
|
||||
|
||||
def _pwd():
|
||||
r = subprocess.run(['sops','-d','D:/vault/clients/dataforth/ad2.sops.yaml'],
|
||||
capture_output=True, text=True, timeout=30, check=True)
|
||||
return yaml.safe_load(r.stdout)['credentials']['password'].replace('\\','')
|
||||
|
||||
PWD = _pwd()
|
||||
|
||||
def run(cmd, timeout=60):
|
||||
c = paramiko.SSHClient()
|
||||
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
c.connect(HOST, username=USER, password=PWD, timeout=15, look_for_keys=False, allow_agent=False)
|
||||
try:
|
||||
stdin, stdout, stderr = c.exec_command(cmd, timeout=timeout)
|
||||
out = stdout.read().decode(errors='replace')
|
||||
err = stderr.read().decode(errors='replace')
|
||||
rc = stdout.channel.recv_exit_status()
|
||||
return rc, out, err
|
||||
finally:
|
||||
c.close()
|
||||
|
||||
def pull(remote_path, local_path):
|
||||
c = paramiko.SSHClient()
|
||||
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
c.connect(HOST, username=USER, password=PWD, timeout=15, look_for_keys=False, allow_agent=False)
|
||||
try:
|
||||
sftp = c.open_sftp()
|
||||
sftp.get(remote_path, local_path)
|
||||
sftp.close()
|
||||
finally:
|
||||
c.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.argv[1] == 'pull':
|
||||
pull(sys.argv[2], sys.argv[3])
|
||||
print(f'[OK] pulled {sys.argv[2]} -> {sys.argv[3]}')
|
||||
else:
|
||||
rc, out, err = run(' '.join(sys.argv[1:]))
|
||||
if out: print(out, end='')
|
||||
if err: print('STDERR:', err, file=sys.stderr, end='')
|
||||
sys.exit(rc)
|
||||
Reference in New Issue
Block a user