diff --git a/README.md b/README.md index 0cec85d..6d01dbc 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,11 @@ This implementation uses `ffi` to export wrapper functions. # TODO 1. Error handling - provide an interface for a custom error handler -2. Solve the FFI DynamicLibrary issue (it appends the .dylib library suffix which breaks macOS) +2. Solve the FFI DynamicLibrary issue (it appends the .dylib library suffix which breaks on recent macOS) 3. Make it more "npm package-y" 4. Implement the full v19 API (and constants!) + + +# Thanks + +Originally inspired by Jürgen Skrotzky's [visa32 project](https://github.com/Jorgen-VikingGod/visa32). diff --git a/n6705b.js b/n6705b.js index 55ffbb3..259e660 100644 --- a/n6705b.js +++ b/n6705b.js @@ -1,22 +1,26 @@ /** - * EEMBC implementation of N6705B control for IoTConnect frameworks. + * Copyright (C) Peter Torelli * - * Copyright (c) EEMBC - * - * Original author: P.Torelli (based on Python code by S.Allen/Dialog) + * Licensed under Apache 2.0 + * + * Interface implementation for Keysight N6705B */ /** + * TODO List: + * * TODO: How do we turn off the emon after sampling automatically? * TODO: Should there be checks to prevent triggering if init() hasn't been * called? (e.g., this.ready) + * TODO: SCPI error-handling is dodgy: rather than send a RESP, I have to check + * the panel of the Keysight for an error. Really? */ const visa = require('./ni-visa.js'), vcon = require('./ni-visa-constants.js'), pause = require('./pause.js'), - debug = require('debug')('n6705b'), + debug = require('debug')('emon'), util = require('util') const MIN_PERIOD_S = 20e-6; @@ -152,17 +156,13 @@ KeysightN6705B.prototype.setup_p = async function (voltageV=3.0, rateHz=1000) { */ KeysightN6705B.prototype.timeAcquire_p = async function (timeS=10) { debug('timeAcquire_p'); - // TODO this needs to be EXTernal for the BNC backpanel input + // TODO This needs to be EXTernal for the BNC backpanel input visa.viWrite(this.inst, 'TRIG:DLOG:SOUR BUS'); visa.viWrite(this.inst, `SENS:DLOG:TIME ${timeS}`); resp = visa.vhQuery(this.inst, 'SENS:DLOG:TIME?') let actualTimeS = parseFloat(resp); - // TODO: Recommended by Steve Allen - await pause(0.5); - // TODO: How do we tell if we run out of disk space? + // TODO How do we tell if we run out of disk space? visa.viWrite(this.inst, 'INIT:DLOG "internal:\\data1.dlog"') - // TODO: Recommended by Steve Allen - await pause(0.5); return actualTimeS; } @@ -173,6 +173,7 @@ KeysightN6705B.prototype.selfTrigger = function () { KeysightN6705B.prototype.downloadData = function () { debug('downloadData'); let resp; + let status; this.off(); resp = visa.vhQuery(this.inst, 'MMEM:ATTR? "internal:\\data1.dlog", "FileSize"'); // TODO: Odd, why suddenly are there quotes in the response? @@ -180,29 +181,34 @@ KeysightN6705B.prototype.downloadData = function () { let bytes = parseInt(resp); debug(`File ${DEFAULT_LOG_FILE} size is ${bytes} bytes`); let buffer = Buffer.alloc(0); - visa.viWrite(this.inst, `MMEM:DATA? "${DEFAULT_LOG_FILE}"`) - let status; + visa.viWrite(this.inst, `MMEM:DATA:DEF? "${DEFAULT_LOG_FILE}"`) // TODO: Load the ENTIRE file into memory could be a bad idea in the future - let check = 0; + const start = process.hrtime(); do { - [status, resp] = visa.viReadRaw(this.inst); - console.log(resp.toString()); - console.log(status.toString(16), resp.length); - check += resp.length; + [status, resp] = visa.viReadRaw(this.inst, 512); buffer = Buffer.concat([buffer, resp], buffer.length + resp.length) + if (status & vcon.VI_ERROR) { + throw new Error("Error reading dlog file", status.toString(16)); + } } while (status && !(status & vcon.VI_ERROR)); - console.log('status ', status) - console.log('buffer sum ', check); + const diff = process.hrtime(start); + const sec = diff[1] / 1e9 + diff[0]; + const tpt = (buffer.length / 1024) / sec; + debug('bytes read ', buffer.length); + debug('FileSize? ', bytes); + debug('speed ', tpt.toPrecision(3), 'KB/s'); // Fast-forward past the header - let startRaw = buffer.indexOf('\n') + '\n'.length; - if (startRaw === 0) { + let offset = buffer.indexOf('\n') + '\n'.length; + if (offset === 0) { throw new Error('Failed to find start of binary data'); } // The 8 bytes after the header are also not needed - console.log('first dword ', buffer.readUInt32BE(startRaw)); - console.log('scnd dword ', buffer.readUInt32BE(startRaw + 4)); - console.log("Skip bytes = ", startRaw + 8); - return buffer.slice(startRaw + 8, bytes); + debug('1st dword ', buffer.readUInt32BE(offset).toString(16)); + debug('2nd dword ', buffer.readUInt32BE(offset + 4).toString(16)); + offset += 8; + debug('Offset ', offset, 'B'); + // BUGBUG: Often more bytes are sent back than there are in the file, why? + return buffer.slice(offset, buffer.length); } KeysightN6705B.prototype.off = function () { diff --git a/ni-visa-constants.js b/ni-visa-constants.js index b1e1167..d513216 100644 --- a/ni-visa-constants.js +++ b/ni-visa-constants.js @@ -1,4 +1,10 @@ -// Constants for the VISA Library 5.8 Specification +/** + * Copyright (C) Peter Torelli + * + * Licensed under Apache 2.0 + * + * Constants for the VISA Library 5.8 Specification by National Instruments + */ const constants = { @@ -648,9 +654,10 @@ const constants = { * 0xBFFF000E = 0x80000000 + 0x3FFF000E = VI_ERROR_INV_OBJECT * = 3221159950 * = -1073807346 + * Returns text string or null */ function decodeStatus (code) { - let key; + let key = null; Object.keys(constants).some((x, y) => { if (x.match(/^VI_(SUCCESS|WARN|ERROR)/)) { if (code == constants[x]) { diff --git a/ni-visa.js b/ni-visa.js index 7ee4088..f73c5ca 100644 --- a/ni-visa.js +++ b/ni-visa.js @@ -1,11 +1,16 @@ -/* - -TODO: - -- Error handling. How we gonna do this? Return a status or just throw? -- debug() +/** + * Copyright (C) Peter Torelli + * + * Licensed under Apache 2.0 + * + * Interface wrapper to the VISA dynamic library. + */ -*/ +/** + * TODO List + * + * TODO: Error handling. Throw? Return status? How should we do this? + */ const debug = require('debug')('ni-visa'), @@ -68,10 +73,20 @@ const libVisa = ffi.Library(dllName, { 'viWrite': [ViStatus, [ViSession, 'string', ViUInt32, ViPUInt32]], }); -function errorHandler (status) { +// TODO: since error handling is undecided, every function calls this +function statusCheck (status) { if (status & vcon.VI_ERROR) { console.log('Warning: VISA Error: 0x' + (status >>> 0).toString(16).toUpperCase()); throw new Error(); + } else { + if (status) { + let str = vcon.decodeStatus(status); + if (str != null) { + debug(`non-error status check: ${status.toString(16)} ${str}`); + } else { + debug(`non-error status check: ${status.toString(16)}`); + } + } } } @@ -79,7 +94,7 @@ function viOpenDefaultRM () { let status; let pSesn = ref.alloc(ViSession); status = libVisa.viOpenDefaultRM(pSesn); - errorHandler(status); + statusCheck(status); return [status, pSesn.deref()]; } @@ -89,7 +104,7 @@ function viFindRsrc (sesn, expr) { let pRetcnt = ref.alloc(ViUInt32); let instrDesc = Buffer.alloc(512); status = libVisa.viFindRsrc(sesn, expr, pFindList, pRetcnt, instrDesc); - errorHandler(status); + statusCheck(status); return [ status, pFindList.deref(), @@ -103,7 +118,7 @@ function viFindNext (findList) { let status; let instrDesc = Buffer.alloc(512); status = libVisa.viFindNext(findList, instrDesc); - errorHandler(status); + statusCheck(status); return [ status, // Fake null-term string @@ -115,37 +130,37 @@ function viOpen (sesn, rsrcName, accessMode=0, openTimeout=2000) { let status; let pVi = ref.alloc(ViSession); status = libVisa.viOpen(sesn, rsrcName, accessMode, openTimeout, pVi); - errorHandler(status); + statusCheck(status); return [status, pVi.deref()]; } function viClose (vi) { let status; status = libVisa.viClose(vi); - errorHandler(status); + statusCheck(status); return status; } // TODO ... assuming viRead always returns a string, probably wrong function viRead (vi, count=512) { - debug(`read (${count})`); let status; let buf = Buffer.alloc(count); let pRetCount = ref.alloc(ViUInt32); status = libVisa.viRead(vi, buf, buf.length, pRetCount) - errorHandler(status); + statusCheck(status); + debug(`read (${count}) -> ${pRetCount.deref()}`); return [status, ref.reinterpret(buf, pRetCount.deref(), 0).toString()]; } -// Returns the raw Buffer object +// Returns the raw Buffer object rather than a decoded string function viReadRaw (vi, count=512) { - debug(`read (${count})`); let status; let buf = Buffer.alloc(count); let pRetCount = ref.alloc(ViUInt32); status = libVisa.viRead(vi, buf, buf.length, pRetCount) - errorHandler(status); - return [status, buf]; + statusCheck(status); + debug(`readRaw: (${count}) -> ${pRetCount.deref()}`); + return [status, buf.slice(0, pRetCount.deref())]; } function viWrite (vi, buf) { @@ -153,8 +168,10 @@ function viWrite (vi, buf) { let status; let pRetCount = ref.alloc(ViUInt32); status = libVisa.viWrite(vi, buf, buf.length, pRetCount) - debug('write:status:', status); - errorHandler(status); + statusCheck(status); + if (pRetCount.deref() != buf.length) { + throw new Error('viWrite length fail') + } return [status, pRetCount.deref()]; } @@ -185,6 +202,7 @@ function vhListResources (sesn, expr='?*') { */ function vhQuery (vi, query) { viWrite(vi, query); + // TODO: return status as well? return viRead(vi)[1]; } diff --git a/test.js b/test.js index 5335642..4dec182 100644 --- a/test.js +++ b/test.js @@ -1,10 +1,18 @@ -let +/** + * Copyright (C) Peter Torelli + * + * Licensed under Apache 2.0 + * + * Just a test area. + */ + + let visa = require('./ni-visa.js'), vcon = require('./ni-visa-constants.js'), n6705b = require('./n6705b.js'), pause = require('./pause.js') -let sampleTime = 10; +let sampleTime = 6; async function main_p () { try { let ks = new n6705b(); @@ -12,20 +20,22 @@ async function main_p () { [actualVolts, actualPeriodS] = await ks.setup_p(3.0, 1/20e-6); actualTimeS = await ks.timeAcquire_p(sampleTime); ks.selfTrigger(); - await pause(actualTimeS + 1); + await pause(11); ks.off(); + await pause(1); let data = ks.downloadData(); console.log('Total bytes sampled', data.length); console.log('Total samples', data.length / 4); ks.close(); console.log(`Voltage ${actualVolts} V, Period ${actualPeriodS * 1e6} usec.`); let energy = 0; - for (let i=0, j=0; i