Image-to-Text
English
custom
ocr
document-ai
information-extraction
key-information-extraction
nameplate
nameplate-ocr
equipment-nameplate
data-center
data-centre
datacenter
mission-critical
mep
electrical
mechanical
equipment-schedule
schedule-verification
asset-register
commissioning
quality-assurance
ups
pdu
switchgear
generator
ats
transformer
busway
battery
crah
crac
chiller
cooling
server-rack
construction
aec
edge-ai
on-device
privacy-preserving
tesseract
browser
Update data-centre nameplates to v2: 31 fields, 55 equipment types, extraction benchmark
2843cff verified Download tests/nameplate-model.test.mjs from constructelligence/data-centre-nameplates: direct link, hf CLI and curl.
- Browser
- Download file 17.6 kB
-
https://huggingface.co/constructelligence/data-centre-nameplates/resolve/main/tests/nameplate-model.test.mjs
- Command line
-
hf download hf://constructelligence/data-centre-nameplates/tests/nameplate-model.test.mjs
-
curl -L -o nameplate-model.test.mjs https://huggingface.co/constructelligence/data-centre-nameplates/resolve/main/tests/nameplate-model.test.mjs
17.6 kB
| // Nameplate parsing, schedule matching and the register CSV (pure functions). | |
| // Run: npm test | |
| import { test } from 'node:test'; | |
| import assert from 'node:assert/strict'; | |
| import { | |
| parseNameplate, voltagesIn, parseEquipmentSchedule, scheduleVoltages, candidatesFor, checkAgainst, statusOf, | |
| codeSimilarity, foldCode, duplicateSerials, registerSummary, registerCsv, typeOf, EQUIPMENT_TYPES, | |
| SAMPLE_SCHEDULE, SAMPLE_PLATES, | |
| } from '../nameplate-model.js'; | |
| const plate = lines => parseNameplate(lines.join('\n')); | |
| const val = (r, k) => r.fields[k]?.value; | |
| const schedule = parseEquipmentSchedule(SAMPLE_SCHEDULE).items; | |
| test('a UPS plate reads into every field', () => { | |
| const r = plate(SAMPLE_PLATES[0]); | |
| assert.equal(r.type, 'ups'); | |
| assert.equal(val(r, 'manufacturer'), 'Northline Power Systems'); | |
| assert.equal(val(r, 'model'), 'NPX-750-480'); | |
| assert.equal(val(r, 'serial'), 'NL26A01937'); | |
| assert.equal(val(r, 'voltage'), '480Y/277 V'); | |
| assert.deepEqual(r.fields.voltage.values, [480, 277]); | |
| assert.equal(val(r, 'phase'), 3); | |
| assert.equal(val(r, 'hz'), 60); | |
| assert.equal(val(r, 'kva'), 750); | |
| assert.equal(val(r, 'kw'), 675); | |
| assert.equal(val(r, 'amps'), 1002); | |
| assert.equal(val(r, 'mfgDate'), '03/2026'); | |
| }); | |
| test('real-world plate wording: S/N, CAT. NO., kV, thousands separators, known makers', () => { | |
| const gen = plate(['CATERPILLAR', 'GENERATOR SET', 'CAT. NO. 3516C-HD', 'S/N: ZBA00412', '13.8 KV 3 PHASE 60 HZ', '3,750 KVA 3000 EKW', 'FLA 1,569']); | |
| assert.equal(gen.type, 'generator'); | |
| assert.equal(val(gen, 'manufacturer'), 'Caterpillar'); | |
| assert.equal(val(gen, 'model'), '3516C-HD'); | |
| assert.equal(val(gen, 'serial'), 'ZBA00412'); | |
| assert.deepEqual(gen.fields.voltage.values, [13800]); | |
| assert.equal(val(gen, 'kva'), 3750); | |
| assert.equal(val(gen, 'kw'), 3000); | |
| assert.equal(val(gen, 'amps'), 1569); | |
| const ch = plate(['AIR-COOLED CHILLER', 'MODEL: AC-1200', 'REFRIGERANT R-513A CHARGE 410 LBS', '460V 3PH 60HZ', 'MCA 612 MOCP 800']); | |
| assert.equal(ch.type, 'chiller'); | |
| assert.equal(val(ch, 'refrigerant'), 'R-513A'); | |
| assert.equal(val(ch, 'mca'), 612); | |
| assert.equal(val(ch, 'mocp'), 800); | |
| assert.equal(val(ch, 'amps'), undefined, 'R-513A and a charge weight are not amps'); | |
| }); | |
| test('voltages: transformer pairs, labelled values, and numbers that are not voltages', () => { | |
| assert.deepEqual(voltagesIn('PRI 13,800 V DELTA SEC 480Y/277 V').values, [480, 277, 13800]); | |
| assert.deepEqual(voltagesIn('VOLTS: 208').values, [208]); | |
| assert.deepEqual(voltagesIn('DWG 480/277 REV 2').values, [], 'a bare pair with no voltage word or unit'); | |
| assert.deepEqual(scheduleVoltages('480 / 415Y/240'), [480, 415, 240]); | |
| assert.deepEqual(scheduleVoltages('13.8kV'), [13800]); | |
| }); | |
| test('"New York" in an address is not the maker York; unknown makers come from a company line', () => { | |
| const r = plate(['ACME SWITCHGEAR CORP', 'NEW YORK NY', 'SWITCHBOARD', 'MODEL SB-4000']); | |
| assert.equal(val(r, 'manufacturer'), 'Acme Switchgear Corp'); | |
| assert.equal(r.type, 'switchgear'); | |
| // A transformer winding is "delta"; that is not the maker Delta. | |
| const t = plate(['VOLTARC TRANSFORMER CORP', 'DRY TYPE TRANSFORMER', 'MODEL VT-2500-HV', 'PRIMARY 13800 V DELTA', 'SECONDARY 480Y/277 V']); | |
| assert.equal(val(t, 'manufacturer'), 'Voltarc Transformer Corp'); | |
| assert.equal(t.type, 'transformer'); | |
| }); | |
| test('codes compare the way OCR misreads them', () => { | |
| assert.equal(foldCode('NPX-75O-48O'), foldCode('NPX750480')); | |
| assert.equal(codeSimilarity('NL26A0I937', 'NL26A01937'), 1); | |
| assert.ok(codeSimilarity('9395P-750', '9395P-750-N-480') >= 0.9, 'option suffix on the plate'); | |
| assert.ok(codeSimilarity('NPX-750-415', 'NPX-750-480') < 0.85, 'a different voltage variant is not the same model'); | |
| }); | |
| test('schedule CSV: header row found below title rows, duplicate tags reported', () => { | |
| const { items, problems } = parseEquipmentSchedule('Data Hall 1 equipment schedule\n\nMark,Equipment,Mfr,Model No.,Volts,kVA\nUPS-1A,UPS,Eaton,9395XR,480,750\nUPS-1A,UPS,Eaton,9395XR,480,750\n'); | |
| assert.equal(items.length, 1); | |
| assert.equal(items[0].manufacturer, 'Eaton'); | |
| assert.equal(items[0].kva, 750); | |
| assert.match(problems[0], /UPS-1A appears more than once/); | |
| assert.match(parseEquipmentSchedule('a,b\n1,2').problems[0], /No header row/); | |
| }); | |
| test('identical units: the first unscanned tag is offered first', () => { | |
| const r = plate(SAMPLE_PLATES[0]); | |
| const first = candidatesFor(r, schedule); | |
| assert.deepEqual(first.slice(0, 4).map(c => c.item.tag), ['UPS-1A', 'UPS-1B', 'UPS-2A', 'UPS-2B']); | |
| const next = candidatesFor(r, schedule, [{ tag: 'UPS-1A' }]); | |
| assert.equal(next[0].item.tag, 'UPS-1B'); | |
| assert.equal(next.find(c => c.item.tag === 'UPS-1A').registered, true); | |
| assert.ok(!first.some(c => c.item.tag.startsWith('GEN')), 'a generator is not a candidate for a UPS plate'); | |
| }); | |
| test('a serial on the schedule decides the match outright', () => { | |
| const items = parseEquipmentSchedule('Tag,Model,Serial\nUPS-1A,NPX-750-480,NL26A00001\nUPS-1B,NPX-750-480,NL26A01937\n').items; | |
| assert.equal(candidatesFor(plate(SAMPLE_PLATES[0]), items)[0].item.tag, 'UPS-1B'); | |
| }); | |
| test('voltage compatibility is a positive signal, but the equipment type still wins', () => { | |
| // The 415 V UPS shares a voltage with the PDU rows (480/415Y/240), yet the | |
| // matching UPS rows must still rank first. | |
| const first = candidatesFor(plate(SAMPLE_PLATES[1]), schedule); | |
| assert.ok(first[0].item.tag.startsWith('UPS'), `first was ${first[0].item.tag}`); | |
| assert.ok(!first.slice(0, 4).some(c => c.item.tag.startsWith('PDU')), 'no PDU outranks the UPS rows'); | |
| }); | |
| test('out-of-range OCR noise is not accepted as a rating', () => { | |
| const r = plate(['ACME', 'SWITCHBOARD', 'MODEL SB-1', '0 KVA', 'SCCR 0', '0 A', '0 OUTLETS']); | |
| assert.equal(val(r, 'kva'), undefined, 'a zero rating is not a rating'); | |
| assert.equal(val(r, 'sccr'), undefined); | |
| assert.equal(val(r, 'amps'), undefined); | |
| assert.equal(val(r, 'outlets'), undefined); | |
| const ok = plate(['ACME', 'SWITCHBOARD', 'MODEL SB-1', '480 V', '65 KAIC']); | |
| assert.equal(val(ok, 'sccr'), 65, 'a plausible value is kept'); | |
| }); | |
| test('checks: the right unit verifies; the wrong-voltage unit is flagged on model and voltage', () => { | |
| const ups1a = schedule.find(i => i.tag === 'UPS-1A'); | |
| const good = checkAgainst(plate(SAMPLE_PLATES[0]), ups1a); | |
| assert.ok(good.every(c => c.status === 'ok'), JSON.stringify(good)); | |
| assert.equal(statusOf(good, 'UPS-1A'), 'verified'); | |
| const bad = checkAgainst(plate(SAMPLE_PLATES[1]), ups1a); | |
| assert.deepEqual(bad.filter(c => c.status === 'mismatch').map(c => c.key), ['model', 'voltage']); | |
| assert.equal(statusOf(bad, 'UPS-1A'), 'mismatch'); | |
| const unread = checkAgainst(parseNameplate('MODEL NPX-750-480'), ups1a); | |
| assert.equal(statusOf(unread, 'UPS-1A'), 'partial'); | |
| assert.equal(statusOf([], ''), 'unmatched'); | |
| }); | |
| test('register: summary, duplicate serials, and a CSV that lists what is still unscanned', () => { | |
| const a = plate(SAMPLE_PLATES[0]); | |
| const ups1a = schedule.find(i => i.tag === 'UPS-1A'); | |
| const reg = [ | |
| { id: 'a', tag: 'UPS-1A', type: 'ups', fields: a.fields, checks: checkAgainst(a, ups1a), status: 'verified', scannedAt: Date.UTC(2026, 9, 2, 14, 30) }, | |
| { id: 'b', tag: '', type: 'ups', fields: a.fields, checks: [], status: 'unmatched', scannedAt: Date.UTC(2026, 9, 2, 14, 35) }, | |
| ]; | |
| assert.deepEqual(duplicateSerials(reg), [['a', 'b']]); | |
| const s = registerSummary(reg, schedule); | |
| assert.equal(s.scannedOfSchedule, 1); | |
| assert.equal(s.verified, 1); | |
| assert.equal(s.unmatched, 1); | |
| assert.equal(s.missing.length, schedule.length - 1); | |
| const rows = registerCsv(reg, schedule).trim().split('\n'); | |
| assert.equal(rows.length, 1 + 2 + schedule.length - 1); | |
| assert.match(rows[1], /^UPS-1A,Verified,UPS,Electrical Room 1A,Northline Power Systems,NPX-750-480,NL26A01937,480Y\/277 V,3,60,750,675,1002,,,,,,,,,,,,,,,,,,,,,,03\/2026,Serial also on another scanned plate,2026-10-02 14:30$/); | |
| assert.match(rows[3], /^UPS-1B,Not scanned yet,/); | |
| }); | |
| test('OCR misreads seen on real lettering are undone before parsing', () => { | |
| const r = parseNameplate(['MODEL NO: NPX- 750- 480', '480¥/277 VAC 3 PH 60 Hw', '460 Vv', '750 XVA / 675 XW', 'FLA 38. 5'].join('\n')); | |
| assert.equal(r.fields.model.value, 'NPX-750-480'); | |
| assert.deepEqual(r.fields.voltage.values, [480, 277, 460]); | |
| assert.equal(r.fields.hz.value, 60); | |
| assert.equal(r.fields.kva.value, 750); | |
| assert.equal(r.fields.kw.value, 675); | |
| assert.equal(r.fields.amps.value, 38.5); | |
| }); | |
| test('MCA and MOCP are checked when the schedule lists them; near-zero-confidence lines are ignored', () => { | |
| const ch = schedule.find(i => i.tag === 'CH-01'); | |
| assert.equal(ch.mca, 612); | |
| const r = parseNameplate([{ text: 'MODEL AC-1200', confidence: 90 }, { text: '460 V 3 PH', confidence: 88 }, { text: 'MCA 612 MOCP 700', confidence: 85 }, | |
| { text: 'REFRIGERANT R-513A', confidence: 90 }, { text: 'MODEL ZZ-9 480 V', confidence: 8 }]); | |
| assert.equal(r.fields.model.value, 'AC-1200', 'the 8% line is texture, not a second model'); | |
| const checks = checkAgainst(r, ch); | |
| assert.deepEqual(checks.filter(c => c.status === 'mismatch').map(c => c.key), ['mocp']); | |
| }); | |
| test('a wide data centre equipment set is recognised, device wording beating its family', () => { | |
| assert.ok(EQUIPMENT_TYPES.length >= 40, 'the taxonomy covers more than the classic dozen'); | |
| const cases = [ | |
| ['FLYWHEEL UPS', 'flywheel'], ['UNINTERRUPTIBLE POWER SUPPLY', 'ups'], ['STATIC TRANSFER SWITCH', 'sts'], | |
| ['AUTOMATIC TRANSFER SWITCH', 'ats'], ['BATTERY CHARGER', 'rectifier'], ['POWER CONVERSION SYSTEM INVERTER', 'inverter'], | |
| ['BATTERY ENERGY STORAGE SYSTEM', 'bess'], ['PARALLELING SWITCHGEAR', 'paralleling'], ['DIESEL GENERATOR SET', 'generator'], | |
| ['FUEL OIL DAY TANK', 'fueltank'], ['SWITCHBOARD', 'switchgear'], ['MOTOR CONTROL CENTER', 'mcc'], | |
| ['POWER METER', 'metering'], ['DRY TYPE TRANSFORMER', 'transformer'], ['SURGE PROTECTIVE DEVICE', 'spd'], | |
| ['POWER FACTOR CORRECTION CAPACITOR', 'capacitor'], ['VARIABLE FREQUENCY DRIVE', 'vfd'], ['ISOLATED PHASE BUS', 'busway'], | |
| ['PANELBOARD', 'panelboard'], ['REMOTE POWER PANEL', 'pdu'], ['BATTERY CABINET', 'battery'], ['LOAD BANK', 'loadbank'], | |
| ['COOLING TOWER', 'coolingtower'], ['AIR-COOLED CHILLER', 'chiller'], ['DRY COOLER', 'condenser'], | |
| ['LIQUID COOLING CDU', 'cdu'], ['REAR DOOR HEAT EXCHANGER', 'cdu'], ['COMPUTER ROOM AIR HANDLER', 'crah'], | |
| ['AIR HANDLING UNIT', 'ahu'], ['HEAT PUMP', 'heatpump'], ['PLATE HEAT EXCHANGER', 'heatexchanger'], | |
| ['CHILLED WATER PUMP', 'pump'], ['CONDENSER WATER PUMP', 'pump'], ['STEAM HUMIDIFIER', 'humidifier'], | |
| ['WATER TREATMENT PLANT', 'watertreatment'], ['DESICCANT DRYER', 'dryer'], ['AIR COMPRESSOR', 'compressor'], | |
| ['FAN COIL UNIT', 'fcu'], ['ROOFTOP UNIT', 'rtu'], ['MAKEUP AIR UNIT', 'mua'], ['FM-200 FIRE SUPPRESSION', 'firesuppression'], | |
| ['VESDA ASPIRATING SMOKE DETECT', 'firealarm'], ['WATER LEAK DETECT', 'leakdetection'], ['BMS CONTROLLER', 'bms'], | |
| ['KVM SWITCH', 'kvm'], ['SERVER RACK ENCLOSURE', 'rack'], ['BLADE SERVER', 'server'], ['NETWORK SWITCH', 'network'], | |
| ['PROTECTIVE RELAY PANEL', 'relay'], ['TRANSFORMER MONITOR DGA', 'tmonitor'], ['NEUTRAL GROUNDING RESISTOR', 'ngr'], | |
| ['ISOLATION TRANSFORMER', 'transformer'], ['K-RATED TRANSFORMER', 'transformer'], ['WATER SOFTENER', 'watertreatment'], | |
| ['REVERSE OSMOSIS SYSTEM', 'watertreatment'], ['EXPANSION TANK', 'expansiontank'], ['AIR SEPARATOR', 'airseparator'], | |
| ['NITROGEN GENERATOR', 'nitrogen'], ['HOT WATER BOILER', 'boiler'], ['THERMAL STORAGE TANK', 'thermalstorage'], | |
| ['EVAPORATIVE COOLER', 'condenser'], ['STATIC BYPASS', 'sts'], ['RACK ATS', 'sts'], | |
| ]; | |
| for (const [text, want] of cases) assert.equal(typeOf(text), want, text); | |
| }); | |
| test('cooling and electrical nameplates read the wider field set', () => { | |
| const sg = plate(['IRONSIDE SWITCHGEAR CORP', 'SWITCHBOARD', 'MODEL IS-4000-MDP', 'SERIAL IS26C01188', | |
| '480Y/277 V 3 PH 60 HZ', 'SCCR 65 KAIC', 'NEMA 3R', '0.8 PF']); | |
| assert.equal(sg.type, 'switchgear'); | |
| assert.equal(val(sg, 'sccr'), 65); | |
| assert.equal(val(sg, 'enclosure'), 'NEMA 3R'); | |
| assert.equal(val(sg, 'pf'), 0.8); | |
| const ch = plate(['POLARIS THERMAL', 'WATER-COOLED CHILLER', 'MODEL PT-WC-900', '460 V 3 PH 60 HZ', | |
| 'REFRIGERANT R-1234ZE CHARGE 620 LBS', 'CAPACITY 500 TONS', 'AIRFLOW 24000 CFM', 'FLOW 2400 GPM']); | |
| assert.equal(ch.type, 'chiller'); | |
| assert.equal(val(ch, 'refrigerant'), 'R-1234ZE'); | |
| assert.equal(val(ch, 'charge'), 620); | |
| assert.equal(val(ch, 'tons'), 500); | |
| assert.equal(val(ch, 'cfm'), 24000); | |
| assert.equal(val(ch, 'gpm'), 2400); | |
| const bat = plate(['ENERSYS', 'BATTERY CABINET', 'MODEL MX-500', '480 V 3 PH', 'CAPACITY 500 AH', 'IP20']); | |
| assert.equal(bat.type, 'battery'); | |
| assert.equal(val(bat, 'batteryAh'), 500); | |
| assert.equal(val(bat, 'enclosure'), 'IP 20'); | |
| }); | |
| test('battery chemistry, listings, speed and rigging weight are read', () => { | |
| const bat = plate(['EXIDE', 'BATTERY STRING', 'MODEL VR-48', 'VRLA 48 CELLS', 'LISTED UL 1989', 'WEIGHT 1250 LBS']); | |
| assert.equal(val(bat, 'batteryType'), 'VRLA'); | |
| assert.equal(val(bat, 'cells'), 48); | |
| assert.equal(val(bat, 'standards'), 'UL 1989'); | |
| assert.equal(val(bat, 'weight'), 567, '1250 lb normalised to kg'); | |
| const pump = plate(['GRUNDFOS', 'END SUCTION PUMP', 'MODEL CR-64', 'RATED SPEED 1750 RPM', 'MOTOR IE3', 'WEIGHT 320 KG']); | |
| assert.equal(pump.type, 'pump'); | |
| assert.equal(val(pump, 'rpm'), 1750); | |
| assert.equal(val(pump, 'weight'), 320); | |
| const li = plate(['SAFT', 'BATTERY CABINET', 'LITHIUM-ION', 'MODEL LI-500', 'IEC 62619']); | |
| assert.equal(val(li, 'batteryType'), 'Lithium-ion'); | |
| assert.equal(val(li, 'standards'), 'IEC 62619'); | |
| }); | |
| test('battery, listing, speed and weight columns are imported and checked', () => { | |
| const { items, problems } = parseEquipmentSchedule( | |
| 'Tag,Type,Model,Voltage,Battery type,Listings,Ah,Cells,RPM,Weight\n' + | |
| 'BAT-1,Battery cabinet,MX-500,480,VRLA,UL 1989,500,48,,\n' + | |
| 'PMP-1,Pump,CR-64,460,,,,,1750,320\n'); | |
| assert.deepEqual(problems, []); | |
| const [bat, pump] = items; | |
| assert.equal(bat.batteryType, 'VRLA'); | |
| assert.equal(bat.standards, 'UL 1989'); | |
| assert.equal(bat.cells, 48); | |
| assert.equal(pump.rpm, 1750); | |
| assert.equal(pump.weight, 320); | |
| const good = checkAgainst(plate(['EXIDE', 'BATTERY CABINET', 'MODEL MX-500', '480 V', 'VRLA 500 AH 48 CELLS', 'UL 1989']), bat); | |
| assert.deepEqual(good.filter(c => c.status !== 'ok'), []); | |
| const bad = checkAgainst(plate(['EXIDE', 'BATTERY CABINET', 'MODEL MX-500', '480 V', 'AGM 400 AH 24 CELLS', 'UL 891']), bat); | |
| assert.deepEqual(bad.filter(c => c.status === 'mismatch').map(c => c.key), ['batteryAh', 'cells', 'batteryType', 'standards']); | |
| }); | |
| test('server-rack nameplates: rack U height, watts and airflow', () => { | |
| const srv = plate(['DELL EMC', 'POWEREDGE R650 SERVER', 'MODEL NO: R650-2U', 'SERIAL NO: DL26R00442', | |
| '480 V 3 PH 60 HZ', '800 W 12.5 A', 'AIRFLOW 120 CFM']); | |
| assert.equal(srv.type, 'server'); | |
| assert.equal(val(srv, 'uHeight'), 2, 'from the model suffix R650-2U'); | |
| assert.equal(val(srv, 'powerW'), 800); | |
| assert.equal(val(srv, 'amps'), 12.5); | |
| assert.equal(val(srv, 'cfm'), 120); | |
| // A kW line must not be read as watts. | |
| const pdu = plate(['VERTIV', 'RACK PDU', 'MODEL MPX-30', '400 V 3 PH', '5.5 KW', '24 OUTLETS']); | |
| assert.equal(val(pdu, 'powerW'), undefined); | |
| assert.equal(val(pdu, 'kw'), 5.5); | |
| const { items } = parseEquipmentSchedule('Tag,Type,Model,W,Rack U\nSRV-1,Server,R650,800,2\n'); | |
| assert.equal(items[0].powerW, 800); | |
| assert.equal(items[0].uHeight, 2); | |
| assert.deepEqual(checkAgainst(srv, items[0]).filter(c => c.status === 'mismatch').map(c => c.key), ['model']); | |
| // A rack PDU: outlets, kW (not watts) and amps. | |
| const rpdu = plate(['VERTIV', 'RACK PDU', 'MODEL NO: MPX-30-C13', 'SERIAL NO: VR26P00117', | |
| '400 V 3 PH 60 HZ', '30 A 5.5 KW', '24 OUTLETS']); | |
| assert.equal(rpdu.type, 'pdu'); | |
| assert.equal(val(rpdu, 'outlets'), 24); | |
| assert.equal(val(rpdu, 'kw'), 5.5); | |
| assert.equal(val(rpdu, 'powerW'), undefined, 'kW is not watts'); | |
| assert.equal(val(rpdu, 'amps'), 30); | |
| const sw = plate(['CISCO', 'CATALYST NETWORK SWITCH', 'MODEL NO: C9300-48P', 'SERIAL NO: FOC26X0011', | |
| '230 V 3 PH 50 HZ', '48-PORT POE 1100 W']); | |
| assert.equal(sw.type, 'network'); | |
| assert.equal(val(sw, 'ports'), 48); | |
| assert.equal(val(sw, 'powerW'), 1100); | |
| }); | |
| test('schedule columns for the extra fields are read and checked', () => { | |
| const { items, problems } = parseEquipmentSchedule('Tag,Type,Model,Voltage,PF,SCCR,Enclosure,Tons,CFM,GPM,Ah\n' + | |
| 'SWB-1,Switchboard,IS-4000,480Y/277,0.8,65,NEMA 3R,,,,,\n' + | |
| 'CH-1,Chiller,PT-900,460,,,,500,24000,2400,\n' + | |
| 'BAT-1,Battery cabinet,MX-500,480,,,,,,,500\n'); | |
| assert.deepEqual(problems, []); | |
| const [swb, ch, bat] = items; | |
| assert.equal(swb.pf, 0.8); | |
| assert.equal(swb.sccr, 65); | |
| assert.equal(swb.enclosure, 'NEMA 3R'); | |
| assert.equal(ch.tons, 500); | |
| assert.equal(ch.cfm, 24000); | |
| assert.equal(ch.gpm, 2400); | |
| assert.equal(bat.batteryAh, 500); | |
| const good = checkAgainst(plate(['ACME SWITCHBOARD', 'SWITCHBOARD', 'MODEL IS-4000', '480Y/277 V', 'PF 0.8', 'SCCR 65 KA', 'NEMA 3R']), swb); | |
| assert.deepEqual(good.filter(c => c.status !== 'ok'), []); | |
| const bad = checkAgainst(plate(['ACME SWITCHBOARD', 'SWITCHBOARD', 'MODEL IS-4000', '480Y/277 V', 'PF 0.9', 'SCCR 42 KA', 'NEMA 1']), swb); | |
| assert.deepEqual(bad.filter(c => c.status === 'mismatch').map(c => c.key), ['pf', 'sccr', 'enclosure']); | |
| }); | |