import pify from 'pify' // import btc from 'better-try-catch' 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) { log = logger({ name: 'i2c-bus', id: opts.id, file: 'src/bus.js', class: 'Bus' }) if (opts.path) opts.ns = { path: opts.path } if (!opts.ns) opts.ns = { path: 'i2c-bus' } opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'ns#s>n' // TODO if port is passed as option then set up tcp with that port if (opts.tcp) { if (typeof opts.tcp === 'number') opts.ts = { port: opts.tcp } else opts.ts = { port: 1776 } opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'ts#s>t' } super(opts) log.debug({ opts: opts }, 'created bus with these opts') this.busnum = opts.busnum || 1 this.i2cbus = i2c.open(this.busnum, () => {}) this._funcs = bus_funcs this.bus = {} this.addBusFuncs(this._funcs) this.addNamespace('bus') this.bus.busFuncs = async (packet) => { packet.functions = this.busFuncs packet.cmd='reply' return packet } this._queue = new PQueue({concurrency: opts.concurrency || 1}) } async init() { await super.init() let count = 0 this._queue.on('active', () => { log.debug(`Queue: working on item #${++count}. Size: ${this._queue.size} Pending: ${this._queue.pending}`) }) } get busFuncs() { return this._funcs} addBusFuncs(funcs) { for (let name in funcs) { let func = funcs[name] this.addBusFunc(name,func) } } addBusFunc(name,func) { this.bus[name] = busFunction.bind(this, func.name || name , func.args || []) this._funcs[name]=func } } // 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 (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 invalide value ${argv} for function ${func}` return true } } else { packet.error = `missing argument, ${arg}, for function ${func}` return true } } }) if (!packet.error) { log.trace({msg:'adding to queue', function:func,arguments:argsV}) let busfunc = pify(this.i2cbus[func].bind(this.i2cbus,...argsV)) let [err,res] = await to(this._queue.add(busfunc)) if (err) packet.error = `error during call to ${func} 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']} }