// -----------------------
// TypeScript [2.6.2]
// USB Library for Node.JS: https://github.com/tessel/node-usb
// -----------------------
import * as USB from 'usb'
import MSeriesReceiverParser from './mSeriesReceiverParser'
const TRANS_ERROR_THRESHOLD = 10
export default class Device {
bulkInEndpoint: USB.InEndpoint | null
bulkInEndpointTestCount: number
callback: ((broadcast: Broadcast) => void)
coreDevice: USB.Device
constructor (device: USB.Device, callback: ((broadcast: Broadcast) => void)) {
this.bulkInEndpoint = null
this.bulkInEndpointTestCount = 0
this.callback = callback
this.coreDevice = device
this.initialize()
}
/*****************************************
* Exposed Controls
*****************************************/
isSameDevice (device: USB.Device) {
return device.busNumber === this.coreDevice.busNumber &&
device.deviceAddress === this.coreDevice.deviceAddress
}
async disconnect () {
try {
await this.stopTransfer()
this.coreDevice.close()
if (process.env.NODE_ENV !== 'production') {
console.log('Device closed')
}
} catch (error) {
if (process.env.NODE_ENV !== 'production') {
console.error('Error closing device\n', error)
}
}
}
/*****************************************
* Control Flow
*****************************************/
async initialize () {
this.coreDevice.open()
this.mapEndpoints()
this.startTransfer()
}
/*****************************************
* Interface and Endpoints
*****************************************/
mapEndpoints () {
let inf = this.coreDevice.interfaces.find(i => i.endpoints.length === 2)
if (!inf) {
throw new Error('Device interfaces unavailable')
}
inf.claim()
this.bulkInEndpoint = inf.endpoints.find(e => e.direction === 'in' && e.transferType === USB.LIBUSB_TRANSFER_TYPE_BULK) as USB.InEndpoint
}
/*****************************************
* Transfer
*****************************************/
startTransfer () {
if (!this.bulkInEndpoint) {
throw new Error('Device endpoint not assigned')
}
this.bulkInEndpoint.timeout = 50
this.bulkInEndpointTestCount = 0
this.bulkInEndpoint.on('data', (data: Buffer) => {
if (data.length > 0) {
this.parse(data)
if (this.bulkInEndpointTestCount < TRANS_ERROR_THRESHOLD) {
this.bulkInEndpointTestCount++
}
}
})
this.bulkInEndpoint.on('error', (error) => {
if (process.env.NODE_ENV !== 'production') {
console.error('Device error\n', error)
}
})
this.bulkInEndpoint.startPoll(0, 4096)
setTimeout(() => { this.checkTransfer() }, 5000)
}
async stopTransfer () {
return new Promise((resolve, reject) => {
if (!this.bulkInEndpoint) {
return reject(Error('Device endpoint not assigned'))
}
this.bulkInEndpoint.stopPoll(() => {
resolve()
})
})
}
checkTransfer () {
if (!this.bulkInEndpoint) {
throw new Error('Device endpoint not assigned')
}
if (this.coreDevice && this.bulkInEndpointTestCount < TRANS_ERROR_THRESHOLD) {
if (process.env.NODE_ENV !== 'production') {
console.error('Device not transmitting data')
}
}
}
/*****************************************
* Parse
*****************************************/
parse (data: Buffer) {
let rawStream = new Uint8Array(data)
let segments = String.fromCharCode.apply(null, rawStream).split(' ')
let firstSegmentValue = parseInt(segments[0], 10)
if (firstSegmentValue <= 200) {
let swValue = parseFloat(segments[6])
if (swValue >= 6.0 && swValue < 7.0) {
try {
let broadcast = MSeriesReceiverParser(segments)
this.callback(broadcast)
} catch (error) {
console.error('M Series parse error\n', error)
}
} else if (swValue >= 8.0 && swValue < 9.0) {
console.error('Incorrect device connected')
}
}
}
}