labjack-nodejs
Version:
nodejs library for using the LabJackM library
1,306 lines (1,204 loc) • 83.9 kB
JavaScript
/**
* Wrapper around LabJack LJM driver.
*
* @author Chris Johnson (chrisjohn404, LabJack Corp.)
**/
const ref = require('ref-napi');
const util = require('util');//
const driverLib = require('./driver_wrapper');
const jsonConstants = require('ljswitchboard-modbus_map');
const ljm_mm = jsonConstants.getConstants();
const driver_const = require('ljswitchboard-ljm_driver_constants');
const allocBuffer = require('allocate_buffer').allocBuffer;
const LIST_ALL_EXTENDED_MAX_NUM_TO_FIND = driver_const.LIST_ALL_EXTENDED_MAX_NUM_TO_FIND;
const ARCH_INT_NUM_BYTES = driver_const.ARCH_INT_NUM_BYTES;
const ARCH_DOUBLE_NUM_BYTES = driver_const.ARCH_DOUBLE_NUM_BYTES;
const ARCH_POINTER_SIZE = driver_const.ARCH_POINTER_SIZE;
const LJM_LIST_ALL_SIZE = driver_const.LJM_LIST_ALL_SIZE;
/**
* Create a new DriverOperationError object.
*
* This encapsulates the code of an error and indicates that the
* error was thrown from the LabJack-nodejs library in the "driver"
* object. The error code's string and description
* are pulled from the ljswitchboard-modbus_map library.
*
* @param {Number} code The raw / uinterpreted error code from LJM.
* @param {string} message An optional string message.
* @param {number} errFrame In situations where array's are being parsed,
* this indicates the indicy.
* @param {array} results Optional parameter passed when some but not
* all results may be valid.
**/
function DriverOperationError(code, message, errFrame, results)
{
this.code = code;
let errorInfo = ljm_mm.getErrorInfo(code);
this.string = errorInfo.string;
this.description = errorInfo.description;
if(typeof(message) !== 'undefined') {
this.message = message.toString();
}
if(typeof(errFrame) !== 'undefined') {
this.errFrame = errFrame;
}
if(typeof(results) !== 'undefined') {
this.results = results;
}
}
util.inherits(DriverOperationError, Error);
DriverOperationError.prototype.name = 'Driver Operation Error - device';
/**
* Create a new DriverInterfaceError object.
*
* Create a new DriverInterfaceError which encapsulates the code of an error
* encountered while communicating with the driver. This is an error thrown by
* labjack-nodejs, not within driver code.
*
* @param {String/Number} description The integer error code or string
* description of the error encountered.
**/
function DriverInterfaceError(description) {
this.description = description;
this.message = description;
}
util.inherits(DriverInterfaceError, Error);
DriverInterfaceError.prototype.name = 'Driver Interface Error - device';
/**
* Constructor for an object acting as LJM driver wrapper.
*/
exports.ljmDriver = function(ljmOverride) {
if(typeof(ljmOverride) !== 'undefined') {
this.ljm = ljmOverride;
} else {
this.ljm = driverLib.getDriver();
}
this.hasOpenAll = driverLib.hasOpenAll();
this.hasGetHandles = driverLib.hasGetHandles();
this.constants = jsonConstants.getConstants();
this.errors = this.constants.errorsByName;
/**
* Dereferences buffers and zips arrays in building listAll return values.
*
* Helper function for the listAll and listAllSync functions to zip arrays
* and dereference native interface buffers containing information about the
* devices that are available for opening.
*
* @param {number} numFound
* @param {Buffer} aDevT Buffer containing a collection of device types as
* provided by LJM.
* @param {Buffer} aConT Buffer containing information about how each device
*
* @param {Buffer} aSN Appropriate Information from LJM call.
* @param {Buffer} aIP Appropriate Information from LJM call.
* @return {array} Array of objects, each with information about an
* available device.
*/
this.buildListAllArray = function(numFound, aDevT, aConT, aSN, aIP) {
let deviceInfoArray = [];
let offset = 0;
let numDevices = numFound.deref();
for(let i = 0; i < numDevices; i++) {
let ipStr = "";
ipStr += aIP.readUInt8(offset+3).toString();
ipStr += ".";
ipStr += aIP.readUInt8(offset+2).toString();
ipStr += ".";
ipStr += aIP.readUInt8(offset+1).toString();
ipStr += ".";
ipStr += aIP.readUInt8(offset).toString();
//Build Dict-array
deviceInfoArray.push(
{
deviceType:aDevT.readInt32LE(offset),
connectionType:aConT.readInt32LE(offset),
serialNumber:aSN.readInt32LE(offset),
ipAddress:ipStr
}
);
offset +=4;
}
return deviceInfoArray;
};
/**
* Retrieves a list of all LabJack devices avaialable for opening.
*
* Function calls either the LJM_ListALL or LJM_ListAllS functions
* asynchronously.
*
* @param {string} deviceType String describing what type of device to
* open. Examples include 'LJM_dtT7'. May also be an integer constant
* corresponding to the device type.
* @param {string} connectionType connectionType String describing what
* type of connection to open. Examples include 'LJM_ctUSB' and
* 'LJM_ctANY'. May also be an integer constant corresponding to the
* appropriate connection medium to use.
* @param {function} onError Function to call if an error is encoutnered
* while enumerating available devices. Must take a single parameter
* (either an integer or string) describing the error encountered.
* @param {function} onSuccess Function to call with the resulting
* enumeration. Should take a single argument: the listing of
8 avilable devices as an array of object.
*/
this.listAll = function(deviceType, connectionType, onError, onSuccess) {
let errorResult;
let devT;
let conT;
let numFound = new ref.alloc('int',1);
let aDeviceTypes = allocBuffer(4*128);
let aConnectionTypes = allocBuffer(4*128);
let aSerialNumbers = allocBuffer(4*128);
let aIPAddresses = allocBuffer(4*128);
//Figure out if we need to augment the input variables
if(arguments.length < 4) {
//Do something smart
devT = "LJM_dtANY"; //
conT = "LJM_ctANY"; //
onError = arguments[0]; //Re-define onError as first argument
onSuccess = arguments[1]; //Re-define onSuccess as second argument
} else {
devT = deviceType;
conT = connectionType;
}
if ((isNaN(devT)) && (isNaN(conT))) {
errorResult = self.ljm.LJM_ListAllS.async(
devT,
conT,
numFound,
aDeviceTypes,
aConnectionTypes,
aSerialNumbers,
aIPAddresses,
function (err, res) {
// if (err) throw err;
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'listAll', err.toString()));
if (res === 0) {
let devArray = self.buildListAllArray(
numFound,
aDeviceTypes,
aConnectionTypes,
aSerialNumbers,
aIPAddresses
);
return onSuccess(devArray);
} else {
return onError(new DriverOperationError(res));
}
}
);
return 0;
} else if ((!isNaN(devT))&&(!isNaN(conT))) {
errorResult = self.ljm.LJM_ListAll.async(
devT,
conT,
numFound,
aDeviceTypes,
aConnectionTypes,
aSerialNumbers,
aIPAddresses,
function (err, res){
// if(err) throw err;
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'listAll', err.toString()));
if(res === 0) {
let devArray = self.buildListAllArray(
numFound,
aDeviceTypes,
aConnectionTypes,
aSerialNumbers,
aIPAddresses
);
return onSuccess(devArray);
} else {
return onError(new DriverOperationError(res));
}
}
);
return 0;
} else {
return onError(new DriverOperationError(self.errors.LJN_INVALID_ARGUMENTS.error,res));
}
};
/**
* Synchronous version of listAll.
*
* @param {string} deviceType String describing what type of device to
* open. Examples include 'LJM_dtT7'. May also be an integer constant
* corresponding to the device type.
* @param {string} connectionType connectionType String describing what
* type of connection to open. Examples include 'LJM_ctUSB' and
* 'LJM_ctANY'. May also be an integer constant corresponding to the
* appropriate connection medium to use.
* @return {array} the listing of avilable devices as an array of object.
* @throws {DriverOperationError} If the input args aren't correct or an error occurs.
**/
this.listAllSync = function(deviceType, connectionType) {
let errorResult;
let devT;
let conT;
let numFound = new ref.alloc('int',1);
let aDeviceTypes = allocBuffer(4*128);
let aConnectionTypes = allocBuffer(4*128);
let aSerialNumbers = allocBuffer(4*128);
let aIPAddresses = allocBuffer(4*128);
//Figure out if we need to augment the input variables
if(arguments.length < 2) {
//Do something smart
devT = "LJM_dtANY"; //
conT = "LJM_ctANY"; //
} else {
devT = deviceType;
conT = connectionType;
}
if ((isNaN(devT)) && (isNaN(conT))) {
errorResult = self.ljm.LJM_ListAllS(
devT,
conT,
numFound,
aDeviceTypes,
aConnectionTypes,
aSerialNumbers,
aIPAddresses
);
} else if ((!isNaN(devT)) && (!isNaN(conT))) {
errorResult = self.ljm.LJM_ListAll(
devT,
conT,
numFound,
aDeviceTypes,
aConnectionTypes,
aSerialNumbers,
aIPAddresses
);
} else {
throw new DriverOperationError(self.errors.LJN_INVALID_ARGUMENTS.error);
}
if (errorResult === 0) {
let devArray = self.buildListAllArray(
numFound,
aDeviceTypes,
aConnectionTypes,
aSerialNumbers,
aIPAddresses
);
return devArray;
} else {
throw new DriverOperationError(errorResult);
}
};
this.interpretResult = function(buffer,type,index) {
let retVar;
function strInterpret(input) {
let output = '';
for(let i = 0; i < input.length; i++) {
if(input.charCodeAt(i) === 0) {
break;
} else {
output += input[i];
}
}
return output;
};
function numInterpret(input) {return input;};
let bufFuncs = {
'STRING': {func:'toString',argA:'utf8',argB:index,argC:index+50,intFunc:strInterpret},
// 'UINT64': {func:'readFloatLE',argA:index,argB:undefined,argC:undefined},
'FLOAT32': {func:'readFloatBE',argA:index,argB:undefined,argC:undefined,intFunc:numInterpret},
'INT32': {func:'readInt32BE',argA:index,argB:undefined,argC:undefined,intFunc:numInterpret},
'UINT32': {func:'readUInt32BE',argA:index,argB:undefined,argC:undefined,intFunc:numInterpret},
'UINT16': {func:'readUInt16BE',argA:index,argB:undefined,argC:undefined,intFunc:numInterpret},
'BYTE': {func:'readUInt8',argA:index,argB:undefined,argC:undefined,intFunc:numInterpret},
};
let funcStr = bufFuncs[type].func;
let argA = bufFuncs[type].argA;
let argB = bufFuncs[type].argB;
let argC = bufFuncs[type].argC;
let intFunc = bufFuncs[type].intFunc;
retVar = intFunc(buffer[funcStr](argA,argB,argC));
return retVar;
};
this.buildListAllExtendedArray = function(context,deviceData,registers,addresses,names,types,numBytes,buffer) {
let retData = {};
let bufferOffset = 0;
// console.log('Raw Data',C_aBytes);
deviceData.forEach(function(devObj,devObjIndex){
let dataArray = [];
let readDataArray = [];
registers.forEach(function(reg,index) {
let readData = context.interpretResult(buffer,types[index],bufferOffset);
// console.log('Result',types[index],bufferOffset,typeof(readData),readData);
let data = {
register: reg,
name: names[index],
address: addresses[index],
val: readData
};
dataArray.push(data);
readDataArray.push(readData);
bufferOffset += numBytes[index];
});
deviceData[devObjIndex].data = dataArray;
deviceData[devObjIndex].registers = registers;
deviceData[devObjIndex].addresses = addresses;
deviceData[devObjIndex].names = names;
deviceData[devObjIndex].values = readDataArray;
});
return deviceData;
};
/**
* Performs a special LJM list all command that gets extra information from
* every device that is found before returning data to the user.
*
* @param {[type]} deviceType [description]
* @param {[type]} connectionType [description]
* @param {[type]} addresses [description]
* @param {string} deviceType String describing what type of device to
* open. Examples include 'LJM_dtT7'. May also be an integer constant
* corresponding to the device type.
* @param {string} connectionType connectionType String describing what
* type of connection to open. Examples include 'LJM_ctUSB' and
* 'LJM_ctANY'. May also be an integer constant corresponding to the
* appropriate connection medium to use.
* @param {function} onError Function to call if an error is encoutnered
* while enumerating available devices. Must take a single parameter
* (either an integer or string) describing the error encountered.
* @param {function} onSuccess Function to call with the resulting
* enumeration. Should take a single argument: the listing of
* avilable devices as an array of object.
*/
this.listAllExtended = function(deviceType, connectionType, registers, onError, onSuccess) {
// Compensate for Auto-
let devT;
let conT;
let regs;
let onErr;
let onSuc;
let message;
let errorCode;
// Intelligently parse the input arguments
if (arguments.length == 2) {
devT = "LJM_dtANY";
conT = "LJM_ctANY";
regs = [];
onErr = arguments[0];
onSuc = arguments[1];
} else if (arguments.length == 3) {
devT = "LJM_dtANY";
conT = "LJM_ctANY";
regs = arguments[0];
onErr = arguments[1];
onSuc = arguments[2];
} else if (arguments.length == 4) {
devT = arguments[0];
conT = arguments[1];
regs = [];
onErr = arguments[2];
onSuc = arguments[3];
} else if (arguments.length == 5) {
devT = arguments[0];
conT = arguments[1];
regs = arguments[2];
onErr = arguments[3];
onSuc = arguments[4];
} else {
message = 'Invalid number of arguments passed to listAllExtra';
errorCode = self.errors.LJN_INVALID_ARGUMENTS.error;
throw new DriverOperationError(self.errors.LJN_INVALID_ARGUMENTS.error, message);
return onErr(buildAsyncError(errorCode, message));
}
if (typeof(regs) !== 'object') {
message = 'Invalid Argument parsed as desired read registers';
throw new DriverOperationError(errorCode, message);
return onErr(buildAsyncError(errorCode, message));
}
if(isNaN(devT)) {
devT = driver_const.deviceTypes[devT];
if(typeof(devT) === 'undefined') {
devT = 0;
}
}
if(isNaN(conT)) {
conT = driver_const.connectionTypes[conT];
if(typeof(conT) === 'undefined') {
conT = 0;
}
}
// Save the maximum number of devices to be found
let maxNumDevices = LIST_ALL_EXTENDED_MAX_NUM_TO_FIND;
// Values to be interpreted by function
let aAddresses = [];
let addressNames = [];
let numBytes = [];
let aNumRegs = [];
let types = [];
let numRecvRegs = 0;
// Values to be sent to C call
let C_DeviceType = devT; // Device type to search for
let C_ConnectionType = conT; // Connection type to search for
let C_NumAddresses = regs.length; // Number of registers to read
let C_aAddresses; // integer array of addresses;
let C_aNumRegs; // integer array of sizes
let C_MaxNumFound = maxNumDevices; // integer
let C_NumFound; // integer pointer to be populated with num found
let C_aDeviceTypes; // integer array to be populated with deviceTypes
let C_aConnectionTypes; // integer array to be populated with connectionTypes
let C_aSerialNumbers; // integer array to be populated with serialNumbers
let C_aIPAddresses; // integer array to be populated with ipAddresses
let C_aBytes; // byte (char) buffer filled with read-data
let errorResult=0;
// Calculate buffer sizes
let addrBuffSize = 4*C_NumAddresses;
let searchBuffSize = 4*maxNumDevices;
// Allocate buffers for variables that are already known
C_aAddresses = allocBuffer(addrBuffSize);
C_aNumRegs = allocBuffer(addrBuffSize);
C_NumFound = new ref.alloc('int',1);
C_aDeviceTypes = allocBuffer(searchBuffSize);
C_aConnectionTypes = allocBuffer(searchBuffSize);
C_aSerialNumbers = allocBuffer(searchBuffSize);
C_aIPAddresses = allocBuffer(searchBuffSize);
let bytesPerRegister = driver_const.LJM_BYTES_PER_REGISTER;
regs.forEach(function(reg,index){
let info = self.constants.getAddressInfo(reg, 'R');
let numRegs = Math.ceil(info.size/bytesPerRegister);
aAddresses.push(info.address);
aNumRegs.push(numRegs);
numBytes.push(numRegs+numRegs);
types.push(info.typeString);
addressNames.push(info.data.name);
// console.log(info)
numRecvRegs += numRegs;
});
// Allocate the receive buffer LJM will use to save extra read data
let aBytesSize = maxNumDevices * numRecvRegs * bytesPerRegister;
C_aBytes = allocBuffer(aBytesSize);
// Save register data to buffers (filling C_aAddresses and C_aNumRegs)
let i;
let bufIndex = 0;
for (i=0; i < aAddresses.length; i++) {
C_aAddresses.writeInt32LE(aAddresses[i],bufIndex);
C_aNumRegs.writeInt32LE(aNumRegs[i],bufIndex);
bufIndex += ARCH_INT_NUM_BYTES;
}
errorResult = self.ljm.LJM_ListAllExtended.async(
C_DeviceType,
C_ConnectionType,
C_NumAddresses,
C_aAddresses,
C_aNumRegs,
C_MaxNumFound,
C_NumFound,
C_aDeviceTypes,
C_aConnectionTypes,
C_aSerialNumbers,
C_aIPAddresses,
C_aBytes,
function (err, res){
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'listAllExtended', err.toString()));
if (res === 0) {
let devArray = self.buildListAllArray(
C_NumFound,
C_aDeviceTypes,
C_aConnectionTypes,
C_aSerialNumbers,
C_aIPAddresses
);
let retArray = self.buildListAllExtendedArray(
self,devArray,regs,aAddresses,addressNames,types,
numBytes,C_aBytes);
return onSuc(retArray);
} else {
// console.log('LJM_ListAllExtended Err');
// console.log(self.errToStrSync(res));
return onErr(buildAsyncError(res));
}
}
);
};
this.listAllExtendedSync = function(deviceType, connectionType, registers) {
let listAllData;
let devT;
let conT;
let regs;
let message;
let errorCode;
// Intelligently parse the input arguments
if (arguments.length === 0) {
devT = "LJM_dtANY";
conT = "LJM_ctANY";
regs = [];
} else if (arguments.length === 1) {
devT = "LJM_dtANY";
conT = "LJM_ctANY";
regs = arguments[0];
} else if (arguments.length === 2) {
devT = arguments[0];
conT = arguments[1];
regs = [];
} else if (arguments.length === 3) {
devT = arguments[0];
conT = arguments[1];
regs = arguments[2];
} else {
message = 'Invalid number of arguments passed to listAllExtendedSync';
errorCode = self.errors.LJN_INVALID_ARGUMENTS.error;
throw new DriverOperationError(errorCode, message);
}
if (typeof(regs) !== 'object') {
message = 'Invalid Argument parsed as desired read registers-listAllExtendedSync';
errorCode = self.errors.LJN_INVALID_ARGUMENTS.error;
throw new DriverOperationError(errorCode, message);
}
if(isNaN(devT)) {
devT = driver_const.deviceTypes[devT];
if(typeof(devT) === 'undefined') {
devT = 0;
}
}
if(isNaN(conT)) {
conT = driver_const.connectionTypes[conT];
if(typeof(conT) === 'undefined') {
conT = 0;
}
}
// Save the maximum number of devices to be found
let maxNumDevices = LIST_ALL_EXTENDED_MAX_NUM_TO_FIND;
// Values to be interpreted by function
let aAddresses = [];
let addressNames = [];
let numBytes = [];
let aNumRegs = [];
let types = [];
let numRecvRegs = 0;
// Values to be sent to C call
let C_DeviceType = devT; // Device type to search for
let C_ConnectionType = conT; // Connection type to search for
let C_NumAddresses = regs.length; // Number of registers to read
let C_aAddresses; // integer array of addresses;
let C_aNumRegs; // integer array of sizes
let C_MaxNumFound = maxNumDevices; // integer
let C_NumFound; // integer pointer to be populated with num found
let C_aDeviceTypes; // integer array to be populated with deviceTypes
let C_aConnectionTypes; // integer array to be populated with connectionTypes
let C_aSerialNumbers; // integer array to be populated with serialNumbers
let C_aIPAddresses; // integer array to be populated with ipAddresses
let C_aBytes; // byte (char) buffer filled with read-data
let errorResult=0;
// Calculate buffer sizes
let addrBuffSize = 4*C_NumAddresses;
let searchBuffSize = 4*maxNumDevices;
// Allocate buffers for variables that are already known
C_aAddresses = allocBuffer(addrBuffSize);
C_aNumRegs = allocBuffer(addrBuffSize);
C_NumFound = new ref.alloc('int',1);
C_aDeviceTypes = allocBuffer(searchBuffSize);
C_aConnectionTypes = allocBuffer(searchBuffSize);
C_aSerialNumbers = allocBuffer(searchBuffSize);
C_aIPAddresses = allocBuffer(searchBuffSize);
let bytesPerRegister = driver_const.LJM_BYTES_PER_REGISTER;
regs.forEach(function(reg,index){
let info = self.constants.getAddressInfo(reg, 'R');
let numRegs = Math.ceil(info.size/bytesPerRegister);
aAddresses.push(info.address);
aNumRegs.push(numRegs);
numBytes.push(numRegs+numRegs);
types.push(info.typeString);
addressNames.push(info.data.name);
// console.log(info)
numRecvRegs += numRegs;
});
// Allocate the receive buffer LJM will use to save extra read data
let aBytesSize = maxNumDevices * numRecvRegs * bytesPerRegister;
C_aBytes = allocBuffer(aBytesSize);
// Save register data to buffers (filling C_aAddresses and C_aNumRegs)
let i;
let bufIndex = 0;
for (i=0; i < aAddresses.length; i++) {
C_aAddresses.writeInt32LE(aAddresses[i],bufIndex);
C_aNumRegs.writeInt32LE(aNumRegs[i],bufIndex);
bufIndex += ARCH_INT_NUM_BYTES;
}
errorResult = self.ljm.LJM_ListAllExtended(
C_DeviceType,
C_ConnectionType,
C_NumAddresses,
C_aAddresses,
C_aNumRegs,
C_MaxNumFound,
C_NumFound,
C_aDeviceTypes,
C_aConnectionTypes,
C_aSerialNumbers,
C_aIPAddresses,
C_aBytes
);
if (errorResult === 0) {
let devArray = self.buildListAllArray(
C_NumFound,
C_aDeviceTypes,
C_aConnectionTypes,
C_aSerialNumbers,
C_aIPAddresses
);
let retArray = self.buildListAllExtendedArray(
self,devArray,regs,aAddresses,addressNames,types,
numBytes,C_aBytes);
return retArray;
} else {
throw new DriverOperationError(errorResult);
}
};
/**
* Internal helper function to prepare the arguments for an OpenAll call.
* @throws {DriverInterfaceError} Thrown if the function can't be called.
* @throws {DriverOperationError} If the input args aren't correct or an error occurs.
*/
function prepareOpenAll(me, deviceType, connectionType) {
if (!self.hasOpenAll) {
throw new DriverInterfaceError(
'openAll is not loaded. Use ListAll and Open functions instead.'
);
}
if (deviceType === undefined || connectionType === undefined) {
throw new DriverOperationError(
self.errors.LJN_INVALID_ARGUMENTS.error,
'Insufficient parameters for OpenAll - DeviceType and ConnectionType required'
);
}
me.numOpened = new ref.alloc('int', 0);
me.aHandles = allocBuffer(ARCH_INT_NUM_BYTES * LJM_LIST_ALL_SIZE);
me.numErrors = new ref.alloc('int', 0);
me.errorHandle = new ref.alloc('int', 0);
me.errors = allocBuffer(ARCH_POINTER_SIZE);
me.devType = driver_const.deviceTypes[deviceType];
me.deviceType = deviceType;
me.connType = driver_const.connectionTypes[connectionType];
me.connectionType = connectionType;
};
/**
* Desc: Internal helper function to extract return data from an OpenAll call.
*/
function extractOpenAllResults(me) {
let results = {
'deviceType': me.deviceType,
'deviceTypeNum': me.devType,
'connectionType': me.connectionType,
'connectionTypeNum': me.connType,
};
// Parse the successfully opened devices.
let numOpened = me.numOpened.deref();
let handles = [];
for(let i = 0; i < numOpened; i++) {
let handle = me.aHandles.readInt32LE(i * ARCH_INT_NUM_BYTES);
handles.push(handle);
}
results.numOpened = numOpened;
results.handles = handles;
// Parse the errors...
let numErrors = me.numErrors.deref();
let failedOpens = [];
results.numErrors = numErrors;
results.failedOpens = failedOpens;
// Parse the errorHandle
let errorHandle = me.errorHandle.readInt32LE(0);
// Read the errors string
let pointerToCharStar = ref.readPointer(me.errors, 0, ARCH_POINTER_SIZE);
let parsedCharStar = ref.readCString(pointerToCharStar, 0);
let errorsString = parsedCharStar;
let errorsObj = {};
try {
errorsObj = JSON.parse(errorsString);
} catch(err) {
errorsObj = {
'exceptions': [],
'networkInterfaces': [],
'returnedDevices': [],
'specificIPs': [],
};
}
// console.log('Parsed Data', errorHandle, errorsObj);
// Save the errors to the results.
results.errorHandle = errorHandle;
results.errors = errorsObj;
return results;
};
// function asyncUnallocateErrorsHandle(handle, callback) {
// self.ljm.LJM_CleanInfo.async(
// handle,
// function (err, res) {
// callback();
// });
// }
// function syncUnallocateErrorsHandle(handle) {
// try {
// self.ljm.LJM_CleanInfo(handle);
// } catch(err) {
// // Error...
// }
// return;
// }
this.cleanInfo = function(handle, onError, onSuccess) {
let bHandle = new ref.alloc('int', handle);
self.ljm.LJM_CleanInfo.async(
bHandle,
function (err, res) {
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'cleanInfo', err.toString()));
if (res !== 0) return onError(new DriverOperationError(res));
onSuccess();
});
}
this.cleanInfoSync = function(handle) {
let bHandle = new ref.alloc('int', handle);
let errorResult = self.ljm.LJM_CleanInfo(bHandle);
if (errorResult !== 0) {
throw new DriverOperationError(errorResult);
}
return;
}
/**
* Desc: Opens all LabJacks of the specified device type and connection type.
*
* @param {int} deviceType The numerical device type
* @param {int} connectionType The numerical connection type
* @param {function} onError Function to call if an error is encountered
* while enumerating available devices. Must take a single parameter
* (either an integer or string) describing the error encountered.
* @param {function} onSuccess Function to call with the resulting
* enumeration. Should take a single argument: a listing representing
* the devices opened as an array of objects.
*/
this.openAll = function(deviceType, connectionType, onError, onSuccess) {
let me = {};
prepareOpenAll(me, deviceType, connectionType);
self.ljm.Internal_LJM_OpenAll.async(
me.devType,
me.connType,
me.numOpened,
me.aHandles,
me.numErrors,
me.errorHandle,
me.errors,
function (err, res) {
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'openAll', err.toString()));
if (res !== 0) return onError(new DriverOperationError(res));
var results = extractOpenAllResults(me);
// return onSuccess(extractOpenAllResults(me));
self.cleanInfo(
results.errorHandle,
onError,
function successFunc(res) {
onSuccess(results);
}
);
}
);
};
this.openAllSync = function(deviceType, connectionType) {
let me = {};
prepareOpenAll(me, deviceType, connectionType);
let errorResult = self.ljm.Internal_LJM_OpenAll(
me.devType,
me.connType,
me.numOpened,
me.aHandles,
me.numErrors,
me.errorHandle,
me.errors
);
if (errorResult !== 0) {
throw new DriverOperationError(errorResult);
}
let results = extractOpenAllResults(me);
self.cleanInfoSync(results.errorHandle);
return results;
};
/**
* Internal helper function to prepare the arguments for an getHandles call.
* @throws {DriverInterfaceError} Thrown if the function can't be called.
*/
function prepareGetHandles(info) {
if (!self.hasGetHandles) {
throw new DriverInterfaceError(
'getHandles is not loaded. The installed LJM library needs to be updated.'
);
}
info.infoHandle = new ref.alloc('int', 0);
info.info = allocBuffer(ARCH_POINTER_SIZE);
return info;
};
function extractGetHandlesResults(info) {
let results = {
infoHandle: infoHandle,
info: info,
infoString: '',
infoObj: {},
};
// Parse the infoHandle
let infoHandle = info.infoHandle.readInt32LE(0);
// Read the errors string
let pointerToCharStar = ref.readPointer(info.info, 0, ARCH_POINTER_SIZE);
let parsedCharStar = ref.readCString(pointerToCharStar, 0);
results.infoString = parsedCharStar;
try {
results.infoObj = JSON.parse(parsedCharStar);
} catch(err) {
results.infoObj = {
'exceptions': [],
'networkInterfaces': [],
'returnedDevices': [],
'specificIPs': [],
};
}
return results;
}
this.getHandles = function(onError, onSuccess) {
let info = {};
prepareGetHandles(info);
self.ljm.Internal_LJM_GetHandles.async(
info.infoHandle,
info.info,
function (err, res) {
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'getHandles', err.toString()));
if (res !== 0) return onError(new DriverOperationError(res));
let results = extractGetHandlesResults(info);
self.cleanInfo(
results.infoHandle,
onError,
onSuccess
);
}
);
}
this.getHandlesSync = function() {
let info = {};
prepareGetHandles(info);
let errorResult = self.ljm.Internal_LJM_GetHandles(
info.infoHandle,
info.info
);
if (errorResult !== 0) {
throw new DriverOperationError(errorResult);
}
let results = extractGetHandlesResults(info);
self.cleanInfoSync(results.infoHandle);
return results.infoObj;
}
/**
* Converts an error number to a string asynchronously.
* @param {number} errNum Error number to be converted to a string.
* @param {function} onError Function to call if an error is encountered
* while converting the provided error number to a string description.
* @param {function} onSuccess Function to call with the string description
* of the error number passed. Should take a single argument: the
* resulting string description.
*/
this.errToStr = function(errNum, onError, onSuccess) {
let errorResult=0;
let strRes = allocBuffer(50);
errorResult = self.ljm.LJM_ErrorToString.async(
errNum,
strRes,
function (err, res){
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'errToStr', err.toString()));
if (res === 0) {
//console.log('strRes: ',ref.readCString(strRes,0));
return onSuccess('Num: '+errNum+', '+ref.readCString(strRes,0));
} else {
//console.log('BAD!',ref.readCString(strRes,0));
return onError('Num: '+errNum+', '+ref.readCString(strRes,0));
}
}
);
return 0;
};
/**
* Synchrnonous version of errToStr.
* @param {number} errNum Error number to be converted to a string.
* @return {string} The string error description corresponding to the
* provided error numebr.
*/
this.errToStrSync = function(errNum) {
let errorResult=0;
let strRes = allocBuffer(50);
errorResult = self.ljm.LJM_ErrorToString(errNum, strRes);
if (errorResult !== 0) {
return 'Num: '+errNum+', '+ref.readCString(strRes,0);
} else {
return 'Num: '+errNum+', '+ref.readCString(strRes,0);
}
};
/**
* Loads driver constants into memory.
*
* @param {function} onError Function to call if an error is encoutnered in
* loading driver constants. Should take a single arugment: an integer
* or string description of the error encoutnered.
* @param {function} onSuccess Function to call after constants have been
* loaded. Should take no arguments.
*/
this.loadConstants = function(onError, onSuccess) {
let errorResult;
errorResult = self.ljm.LJM_LoadConstants.async(
function (err, res){
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'loadConstants', err.toString()));
if (res === 0) {
return onSuccess();
} else {
return onError(new DriverOperationError(res));
}
}
);
return 0;
};
/**
* Calls the LJM_LoadConstants function synchronously.
*
* @throws {DriverOperationError} Error thrown if the driver could not load
* constants, likely because of file system issues.
*/
this.loadConstantsSync = function() {
let errorResult;
errorResult = self.ljm.LJM_LoadConstants();
if (errorResult !== 0) {
throw new DriverOperationError(errorResult);
} else {
return 0;
}
};
/**
* Close all open LabJack devices.
*
* @param {function} onError Function to call if an error is encountered
* while closing devices. Should take a single arugment: a description
* of the error encountered as a string description or integer error
* number.
* @param {function} onSuccess Function to call after all devices have
* been closed. Should take no arguments.
*/
this.closeAll = function(onError, onSuccess) {
let errorResult;
errorResult = self.ljm.LJM_CloseAll.async(
function (err, res){
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'closeAll', err.toString()));
if (res === 0) {
return onSuccess();
} else {
return onError(new DriverOperationError(res));
}
}
);
return 0;
};
/**
* Synchronous version of closeAll.
*/
this.closeAllSync = function() {
let errorResult;
errorResult = self.ljm.LJM_CloseAll();
if (errorResult !== 0) {
return errorResult;
} else {
return 0;
}
};
/**
* Read an operational configuration setting for LJM.
*
* @param {string} parameter The name of the configuration setting to reads.
* @param {function} onError Function to call if an error is encountered
* while reading this configuration setting. This function should
* take a single argument: a string error description or number
* error code.
* @param {function} onSuccess Function to call after the configuration
* setting has been applied. Should take a single argument: the value
* of the configuration setting as read from LJM.
*/
this.readLibrary = function(parameter, onError, onSuccess) {
if (isNaN(parameter)) {
let errorResult;
let returnVar = new ref.alloc('double',1);
errorResult = self.ljm.LJM_ReadLibraryConfigS.async(
parameter,
returnVar,
function (err, res){
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'readLibrary', err.toString()));
if (res === 0) {
return onSuccess(returnVar.deref());
} else {
return onError(new DriverOperationError(res));
}
}
);
return 0;
} else {
return onError(new DriverOperationError(self.errors.LJN_INVALID_ARGUMENTS.error));
}
};
/**
* Synchronous version of readLibrary.
*
* @param {string} parameter The name of the configuration setting to read.
* @return {number} The value of the provided configuration setting.
* @throws {DriverOperationError} Thrown if an exception is encountered in
* the LJM driver itself.
*/
this.readLibrarySync = function(parameter) {
if(isNaN(parameter)) {
let errorResult;
//Allocate a buffer for the result
let returnVar = new ref.alloc('double',1);
//Clear the buffer
returnVar.fill(0);
errorResult = self.ljm.LJM_ReadLibraryConfigS(
parameter,
returnVar
);
// console.log('readLibrarySync', parameter, returnVar.deref(), errorResult);
if (errorResult !== 0) {
return errorResult;
}
return returnVar.deref();
} else {
throw new DriverOperationError(self.errors.LJN_INVALID_ARGUMENTS.error);
}
};
this.readLibraryS = function (parameter, onError, onSuccess) {
if (isNaN(parameter)) {
let errorResult;
//Allocate a buffer for the result
let strBuffer = allocBuffer(driver_const.LJM_MAX_NAME_SIZE);
errorResult = self.ljm.LJM_ReadLibraryConfigStringS.async(
parameter,
strBuffer,
function (err, res){
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'readLibraryS', err.toString()));
if ( res === 0 ) {
//Calculate the length of the string
// var i=0;
// while(strBuffer[i] !== 0) {
// i++;
// }
// return onSuccess(strBuffer.toString('utf8',0,i));
return onSuccess(ref.readCString(strBuffer));
} else {
return onError(new DriverOperationError(res));
}
}
);
return 0;
} else {
return onError(new DriverOperationError(self.errors.LJN_INVALID_ARGUMENTS.error));
}
};
this.readLibrarySSync = function (parameter) {
if(isNaN(parameter)) {
let errorResult;
//Allocate a buffer for the result
let strBuffer = allocBuffer(driver_const.LJM_MAX_NAME_SIZE);
errorResult = self.ljm.LJM_ReadLibraryConfigStringS(
parameter,
strBuffer
);
if (errorResult === 0) {
//Calculate the length of the string
// var i=0;
// while(strBuffer[i] !== 0) {
// i++;
// }
// return strBuffer.toString('utf8',0,i);
return ref.readCString(strBuffer);
}
// return strBuffer.toString('utf8',0,i);
return errorResult;
} else {
throw new DriverOperationError(self.errors.LJN_INVALID_ARGUMENTS.error);
}
};
/**
* Calls the LJM_WriteLibraryConfigS function asynchronously.
*
* @param {number/string} parameter The constant to be read.
* @param {number} value The value to write.
* @param {function} onError Function called on error.
* @param {function} onSuccess Function called on success.
*/
this.writeLibrary = function (parameter, value, onError, onSuccess) {
let errorResult;
if((isNaN(parameter))&&(isNaN(value))) {
let enums = driver_const.LJM_LIBRARY_CONSTANTS;
let isValid = typeof(enums[parameter]);
if ( isValid !== 'undefined') {
isValid = typeof(enums[parameter][value]);
if ( isValid !== 'undefined') {
value = enums[parameter][value];
}
}
}
if ((isNaN(parameter))&&(!isNaN(value))) {
errorResult = self.ljm.LJM_WriteLibraryConfigS.async(
parameter,
value,
function (err, res) {
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'writeLibrary', err.toString()));
if (res === 0) {
return onSuccess();
} else {
return onError(new DriverOperationError(res));
}
}
);
return 0;
} else if((isNaN(parameter))&&(isNaN(value))) {
errorResult = self.ljm.LJM_WriteLibraryConfigStringS.async(
parameter,
value,
function (err, res) {
if (err) onError(new DriverOperationError(self.errors.LJN_UNEXPECTED_ASYNC_CALL_ERROR.error, 'writeLibrary', err.toString()));
if (res === 0) {
return onSuccess();
} else {
return onError(new DriverOperationError(res));
}
}
);
return 0;
} else {
return onError(new DriverOperationError(self.errors.LJN_INVALID_ARGUMENTS.error));
}
};
/**
* Calls the LJM_WriteLibraryConfigS function synchronously.
*
* @param {number/string} parameter The constant to be read.
* @param {number} value The value to write.
*/
this.writeLibrarySync = function (parameter, value) {
let errorResult;
if ((isNaN(parameter))&&(!isNaN(value))) {
errorResult = self.ljm.LJM_WriteLibraryConfigS(
parameter,
value
);
} else if ((isNaN(parameter))&&(isNaN(value))) {
errorResult = self.ljm.LJM_WriteLibraryConfigStringS(
parameter,
value
);
} else {
throw new DriverOperationError(self.errors.LJN_INVALID_ARGUMENTS.error);
}
//Check for an error from driver & throw error
if (errorResult !== 0) {
throw new DriverOperationError(errorResult);
} else {
return errorResult;
}
};
/**
* Log an event through LJM's internal logging system.
*
* @param {number} level The severity of the event to report. Should
* correspond to a driver severity level constant.
* @param {string} str Description of the event.
* @param {function} onError Function to call if the log could not be
* updated successfully. Should take a single argument: either a string
* descriptio