UNPKG

lunijs

Version:

Javascript client for Arduino LuniLib device drivers.

368 lines (310 loc) 11.6 kB
const log4js = require("log4js"); const path = require("path"); const thisModule = path.basename(module.filename,".js"); const log = log4js.getLogger(thisModule); log.setLevel('DEBUG'); /** * These Firmata Sysex command codes identify the DeviceDriver messages. */ let SYSEX = { 'DEVICE_QUERY' : 0x30, 'DEVICE_RESPONSE' :0x31 }; /** * These action codes indicate the type of DeviceDriver query we are making */ let ACTION = { 'OPEN' : 0, 'READ' : 1, 'WRITE' : 2, 'CLOSE' : 3 }; /** * The Device Action Flags enable various extensions to the meanings of * the action codes. */ let DAF = { 'NONE' : 0x0, 'FORCE' : 0x1, 'MILLI_RUN' : 0xC, 'MILLI_STOP' : 0xD, 'MICRO_RUN' : 0xE, 'MICRO_STOP' : 0xF }; /** * These register names and codes indicate the specific item of interest * during DeviceDriver reads and writes. CDR code values are expected * to fall within -1..-255 in order to avoid conflict with device specific * registers which use the values >= 0. */ let CDR = { 'Reset' : -1, /* Reset all state in the device driver for the specified unit number */ 'DriverVersion' : -2, /* Get driver name and version */ 'LibraryVersion' : -3, /* Get library name and version */ 'UnitNamePrefix' : -4, /* Get/set the name with which the units of this device are opened */ 'Configure' : -5, /* Get/set configuration of a logical unit number instance */ 'Intervals' : -6, /* Get/set current timer intervals for this device */ 'Stream' : -7, /* Read or write bytes using the "primary" data stream source or sink */ 'Manufacturer' : -8, /* Get the manufacturer id associated with the device (PCiSIG, ?, ?) */ 'DeviceID' : -9, /* Unique ID for the component attached to the open unit number */ 'Debug' : -255 /* Do something helpful for debugging ... */ }; /** * Define the messge offsets common to all of the DEVICE_QUERY and * DEVICE_RESPONSE messages. */ let MO = { ACTION : 0, OPTIONS : 1, HANDLE : 1, REGISTER : 3, REQUESTED_COUNT : 5, STATUS : 7, DATA : 9 }; //-------------------- /** * This class represents a Firmata Device Query Open message created by the * local client prior to sending it to a remote device driver. */ class DeviceQueryOpen { constructor(unitName, flags, options) { this.action = ACTION.OPEN; this.flags = flags; this.options = options; this.register = 0; this.requestedByteCount = 0; this.unitName = unitName; } toByteArray() { let msgBody = new Buffer(256); msgBody.writeUInt8(((this.flags & 0xF)<<4) | (this.action & 0xF),MO.ACTION); msgBody.writeUInt16LE(this.options,MO.OPTIONS); msgBody.writeInt16LE(0,MO.REGISTER); msgBody.writeUInt16LE(0, MO.REQUESTED_COUNT); msgBody.writeUInt16LE(0,MO.STATUS); msgBody.write(this.unitName,MO.DATA,'utf8'); msgBody.writeUInt8(0,MO.DATA+this.unitName.length); let s = msgBody.toString("base64",0,MO.DATA+this.unitName.length); let encodedMsgBody = Uint8Array.from(s, x => x.charCodeAt(0)); let msgArray = [SYSEX.DEVICE_QUERY, ...encodedMsgBody]; return msgArray; } } /** * This class represents a Firmata Device Response Open message received from * a remote device driver after a call to open(unitName,flags,options). */ class DeviceResponseOpen { constructor(msgBody) { this.action = msgBody.readUInt8(MO.ACTION) & 0xF; this.flags = (msgBody.readUInt8(MO.ACTION) & 0xF0) >> 4; this.options = msgBody.readUInt16LE(MO.OPTIONS); this.register = msgBody.readUInt16LE(MO.REGISTER); this.requestedByteCount = msgBody.readUInt16LE(MO.REQUESTED_COUNT); this.status = msgBody.readInt16LE(MO.STATUS); if (this.status >= 0) { this.handle = this.status; } if (msgBody.length > MO.DATA) { let datablock = msgBody.slice(MO.DATA,msgBody.length-1); this.unitName = datablock.toString("utf8"); } } } //-------------------- /** * This class represents a Firmata Device Query Read message created by the * local client prior to sending it to a remote device driver. */ class DeviceQueryRead { constructor(handle, flags, reg, count) { this.action = ACTION.READ; this.handle = handle; this.flags = flags; this.register = reg; this.requestedByteCount = count; } toByteArray() { let msgBody = new Buffer(256); msgBody.writeUInt8(((this.flags & 0xF)<<4) | (this.action & 0xF),MO.ACTION); msgBody.writeUInt16LE(this.handle,MO.HANDLE); msgBody.writeInt16LE(this.register,MO.REGISTER); msgBody.writeUInt16LE(this.requestedByteCount, MO.REQUESTED_COUNT); msgBody.writeUInt16LE(0,MO.STATUS); let s = msgBody.toString("base64",0,MO.DATA); let encodedMsgBody = Uint8Array.from(s, x => x.charCodeAt(0)); let msgArray = [SYSEX.DEVICE_QUERY, ...encodedMsgBody]; return msgArray; } } /** * This class represents a Firmata Device Response Read message received from * a remote device driver after a call to read(handle, flags, reg, count). */ class DeviceResponseRead { constructor(msgBody) { this.action = msgBody.readUInt8(MO.ACTION) & 0xF; this.flags = (msgBody.readUInt8(MO.ACTION) & 0xF0) >> 4; this.handle = msgBody.readUInt16LE(MO.HANDLE); this.register = msgBody.readInt16LE(MO.REGISTER); this.requestedByteCount = msgBody.readUInt16LE(MO.REQUESTED_COUNT); this.status = msgBody.readInt16LE(MO.STATUS); if (this.status >= 0) { this.actualByteCount = this.status; } else { this.actualByteCount = 0; } if (msgBody.length > MO.DATA) { this.datablock = Buffer.from(msgBody.slice(MO.DATA,msgBody.length)); } else { this.datablock = null; } } } //-------------------- /** * This class represents a Firmata Device Query Write message created by the * local client prior to sending it to a remote device driver. */ class DeviceQueryWrite { constructor(handle, flags, reg, count,buf) { this.action = ACTION.WRITE; this.handle = handle; this.flags = flags; this.register = reg; this.requestedByteCount = count; this.datablock = Buffer.from(buf); } toByteArray() { let msgBody = new Buffer(256); msgBody.writeUInt8(((this.flags & 0xF)<<4) | (this.action & 0xF),MO.ACTION); msgBody.writeUInt16LE(this.handle,MO.HANDLE); msgBody.writeInt16LE(this.register,MO.REGISTER); msgBody.writeUInt16LE(this.requestedByteCount, MO.REQUESTED_COUNT); msgBody.writeUInt16LE(0,MO.STATUS); this.datablock.copy(msgBody,MO.DATA); let s = msgBody.toString("base64",0,MO.DATA+this.requestedByteCount); let encodedMsgBody = Uint8Array.from(s, x => x.charCodeAt(0)); let msgArray = [SYSEX.DEVICE_QUERY, ...encodedMsgBody]; return msgArray; } } /** * This class represents a Firmata Device Response Write message received from * a remote device driver after a call to read(handle, flags, reg, count). */ class DeviceResponseWrite { constructor(msgBody) { this.action = msgBody.readUInt8(MO.ACTION) & 0xF; this.flags = (msgBody.readUInt8(MO.ACTION) & 0xF0) >> 4; this.handle = msgBody.readUInt16LE(MO.HANDLE); this.register = msgBody.readInt16LE(MO.REGISTER); this.requestedByteCount = msgBody.readUInt16LE(MO.REQUESTED_COUNT); this.status = msgBody.readInt16LE(MO.STATUS); if (this.status >= 0) { this.actualByteCount = this.status; } else { this.actualByteCount = 0; } } } //-------------------- /** * This class represents a Firmata Device Query Close message created by the * local client prior to sending it to a remote device driver. */ class DeviceQueryClose { constructor(handle, flags) { this.action = ACTION.CLOSE; this.handle = handle; this.flags = flags; } toByteArray() { let msgBody = new Buffer(MO.DATA); msgBody.writeUInt8(((this.flags & 0xF)<<4) | (this.action & 0xF),MO.ACTION); msgBody.writeUInt16LE(this.handle,MO.HANDLE); msgBody.writeInt16LE(0,MO.REGISTER); msgBody.writeUInt16LE(0,MO.REQUESTED_COUNT); msgBody.writeUInt16LE(0,MO.STATUS); let s = msgBody.toString("base64",0,MO.DATA); let encodedMsgBody = Uint8Array.from(s, x => x.charCodeAt(0)); let msgArray = [SYSEX.DEVICE_QUERY, ...encodedMsgBody]; return msgArray; } } /** * This class represents a Firmata Device Response Close message received from * the remote device driver after a call to close(handle). */ class DeviceResponseClose { constructor(msgBody) { this.action = msgBody.readUInt8(MO.ACTION) & 0xF; this.flags = (msgBody.readUInt8(MO.ACTION) & 0xF0) >> 4; this.handle = msgBody.readUInt16LE(MO.HANDLE); this.status = msgBody.readInt16LE(MO.STATUS); } } //-------------------- class SemVer { /** * Build a SemVer object based on the byte buffer presented. * @param buf integer array of values containing version numbers and labels * */ constructor(buf) { if (buf.length < 6) { this.itemName = "FormatError"; this.version = [0,0,0]; this.preReleaseLabel = "FormatError"; this.buildLabel = "FormatError"; return; } let offset = 0; let byteIndex = 0; // name while (buf[byteIndex++] !== 0) {} log.trace(`offset: ${offset}, count: ${byteIndex-offset}`); let intElements = buf.slice(offset, byteIndex-1); this.itemName = intElements.toString("utf8"); // version numbers let count = buf[byteIndex++]; offset = byteIndex; byteIndex += count; log.trace(`offset: ${offset}, count: ${byteIndex-offset}`); this.version = []; for (let idx = 0; idx < count; idx++) { this.version[idx] = buf[offset+idx] & 0xFF; } // prerelease label offset = byteIndex; while (buf[byteIndex++] !== 0) {} log.trace(`offset: ${offset}, count: ${byteIndex-offset}`); intElements = buf.slice(offset, byteIndex-1); this.preReleaseLabel = intElements.toString("utf8"); // build label offset = byteIndex; while (buf[byteIndex++] !== 0) {} log.trace(`offset: ${offset}, count: ${byteIndex-offset}`); intElements = buf.slice(offset, byteIndex-1); this.buildLabel = intElements.toString("utf8"); } toString() { let result = `${this.itemName} ${this.version[0]}.${this.version[1]}.${this.version[2]}`; if (this.preReleaseLabel.length > 0) { result += `-${this.preReleaseLabel}`; } if (this.buildLabel.length > 0) { result += `+${this.buildLabel}`; } return result; } } //-------------------- module.exports = {SYSEX, ACTION, DAF, MO, CDR, SemVer, DeviceQueryOpen, DeviceResponseOpen, DeviceQueryRead, DeviceResponseRead, DeviceQueryWrite, DeviceResponseWrite, DeviceQueryClose, DeviceResponseClose };