Skip to content

Commit

Permalink
Copyright and cleanup.
Browse files Browse the repository at this point in the history
  • Loading branch information
petertorelli committed Oct 4, 2019
1 parent 16b3036 commit 6678f71
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 56 deletions.
7 changes: 6 additions & 1 deletion README.md
Expand Up @@ -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).
58 changes: 32 additions & 26 deletions 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;
Expand Down Expand Up @@ -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;
}

Expand All @@ -173,36 +173,42 @@ 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?
resp = resp.replace(/"/g, '');
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('</dlog>\n') + '</dlog>\n'.length;
if (startRaw === 0) {
let offset = buffer.indexOf('</dlog>\n') + '</dlog>\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 () {
Expand Down
11 changes: 9 additions & 2 deletions 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 = {

Expand Down Expand Up @@ -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]) {
Expand Down
60 changes: 39 additions & 21 deletions 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'),
Expand Down Expand Up @@ -68,18 +73,28 @@ 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)}`);
}
}
}
}

function viOpenDefaultRM () {
let status;
let pSesn = ref.alloc(ViSession);
status = libVisa.viOpenDefaultRM(pSesn);
errorHandler(status);
statusCheck(status);
return [status, pSesn.deref()];
}

Expand All @@ -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(),
Expand All @@ -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
Expand All @@ -115,46 +130,48 @@ 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) {
debug('write:', 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()];
}

Expand Down Expand Up @@ -185,6 +202,7 @@ function vhListResources (sesn, expr='?*') {
*/
function vhQuery (vi, query) {
viWrite(vi, query);
// TODO: return status as well?
return viRead(vi)[1];
}

Expand Down
22 changes: 16 additions & 6 deletions test.js
@@ -1,31 +1,41 @@
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();
ks.init();
[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<data.length; i+=4, j+=actualPeriodS) {
console.log(j, data.readFloatBE(i));
// why always 1 byte off
for (let i=0, j=0; i<data.length-4; i+=4, j+=actualPeriodS) {
//console.log(i, j, data.readFloatBE(i));
energy += (actualVolts * data.readFloatBE(i)) * actualPeriodS;
}
console.log(data.length);
console.log(energy, data.length / 4 * actualPeriodS);
console.log((energy * 1e6).toPrecision(3), (data.length / 4 * actualPeriodS).toPrecision(3));
} catch (error) {
console.error(error.stack);
}
Expand Down

0 comments on commit 6678f71

Please sign in to comment.