UNPKG

labjack-nodejs

Version:

nodejs library for using the LabJackM library

1,306 lines (1,204 loc) 83.9 kB
/** * 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