uci-i2c-bus/src/bus.js

154 lines
4.5 KiB
JavaScript

import pify from 'pify'
import to from 'await-to-js'
import i2c from 'i2c-bus'
import PQueue from 'p-queue'
import Base from '@uci/base'
import logger from '@uci-utils/logger'
let log = {}
class I2CBus extends Base {
constructor(opts) {
opts.path =opts.path || 'i2c-bus' // always a pipe by default
if (typeof opts.port === 'boolean') opts.port=1776
opts.name = opts.name || 'i2c-bus'
super(opts)
log = logger({
name: 'i2c-bus',
id: this.id,
file: 'src/bus.js',
class: 'Bus'
})
this.busnum = opts.busnum || 1
this.i2cbus = i2c.open(this.busnum, () => {})
this._funcs = bus_funcs
this.bus = {}
this.addBusFuncs()
this.addNamespace('bus')
this.bus.busFuncs = async (packet) => {
packet.functions = this.busFuncs
packet.cmd='reply'
return packet
}
this.bus.ack = async function (){
//
let bus = await this.bus.scan()
if (bus.error || !bus.response.length) bus.ack =false
else bus.ack = true
return bus
},
this.ackInterval = opts.ackInterval!=null ? opts.ackInterval : 5
this._queue = new PQueue({concurrency: opts.concurrency || 1})
this.ready.addObserver('bus:ack')
} // constructor
async init() {
await super.init()
let count = 0
const ack = (await this.bus.ack.call(this)).ack
this.emit('bus:ack',ack) // will be picked up by ready observer
this.autoAck(this.ackInterval) // if auto on then will notice when there is issue/connection with i2cbus
this._queue.on('active', () => {
log.debug(`Queue: working on item #${++count}. Size: ${this._queue.size} Pending: ${this._queue.pending}`)
})
}
get busFuncs() { return this._funcs}
autoAck(interval) {
if (interval>.9) this._autoAck = setInterval(async ()=> {
let ack = (await this.bus.ack.call(this)).ack
this.emit('bus:ack',ack) // will be picked up by ready observer
},interval*1000)
else clearInterval(this._autoAck)
}
addBusFuncs() {
for (let alias in this.busFuncs) {
let func = this.busFuncs[alias]
this.addBusFunc(alias,func.name || alias, func.args)
}
}
addBusFunc(alias,name, args) {
if (!name) name=alias
if (Array.isArray(name)) {
args=name
name=alias
}
this.bus[alias] = busFunction.bind(this, alias, pify(this.i2cbus[name]), args ||[])
}
} // end of Bus Packet Class
export default I2CBus
const validateArg = function (arg,value) {
let valid = false
switch (arg) {
case 'address':
valid = Number.isInteger(value) && value >=0 && value <= 119
break
case 'length':
case 'cmd':
valid = Number.isInteger(value)
break
case 'byte':
valid = Number.isInteger(value) && value >= 0 && value <= 255
break
case 'word':
valid = Number.isInteger(value) && value >= 0 && value <= 65535
break
case 'bit':
valid = Number.isInteger(value) && (value === 0 || value === 1)
break
case 'buffer':
valid = Buffer.isBuffer(value)
}
return valid
}
async function busFunction (name, func, args=[],packet={}) {
let argsV = []
args.some(arg => {
if (packet.args) {
let argv = packet.args[arg]
if (argv != null) {
if(validateArg(arg,argv)) argsV.push(argv)
else {
packet.error = `argument ${arg} has an invalid value ${argv} for function ${name}`
return true
}
} else {
packet.error = `missing argument, ${arg}, for function ${name}`
return true
}
}
})
// this.emit('log', {level:'i2c', packet:packet, error:packet.error, msg:'packet going to bus'})
if (!packet.error) {
log.trace({msg:'adding to queue', function:name,arguments:argsV})
let [err,res] = await to(this._queue.add(() => func.call(this.i2cbus,...argsV)))
if (err) packet.error = `error during call to ${name} with arguments ${JSON.stringify(packet.args)}: ${err}`
else packet.response = res
}
packet.cmd = 'reply'
return packet
}
// default function set
const bus_funcs = {
scan: {},
close: {},
methods: {name:'i2cFuncs'},
deviceId: {args:['address']},
readRaw: {name:'i2cRead', args:['address','length','buffer']},
writeRaw: {name:'i2cWrite', args:['address','length','buffer']},
read: {name:'readByte', args:['address','cmd']},
write: {name:'writeByte', args:['address','cmd','byte']},
read2: {name:'readWord', args:['address','cmd']},
write2: {name:'writeWord', args:['address','cmd','word']},
receive: {name:'receiveByte', args:['address']},
send: {name:'sendByte', args:['address','byte']}
}