import MCP230XX from './mcp230xx' import { byteFormat } from '@uci-utils/byte' import logger from '@uci-utils/logger' let log = {} log = logger({ file: 'src/mcp230xxi.js', class: 'MCP230XXi', name: 'mcp-interrupt', }) // if opts.iport not set then will be generated based on pin number class MCP230XXi extends MCP230XX { constructor(pins, options={}) { if (!Array.isArray(pins)) { options = pins; pins = options.interrupt.pins} // if pins sent via .interrupt.pins let opts = Object.assign({},options) // don't allow passed options to mutate if (opts.interrupt) delete opts.interrupt.pins // if .interrupt was passed then .pins must be removed delete opts.sockets // .sockets is used by uci base so clear it if was used by enduser and sent by mistake log.debug({msg:'passed options before setting',options:opts}) // if iport or ipath is set then all interrupts have a single consolidating socket if (typeof opts.iport === 'number' || opts.ipath) { if (typeof opts.iport === 'number') { opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'inter#c>t' opts.inter = { port: opts.iport } opts.inter.host = opts.ihost || opts.host } if (opts.ipath) { opts.inter = { path: opts.ipath || 'interrupt' } opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + 'inter#c>n' } } else { // otherwise each pin will have its own socket so make a client for each pins.forEach((pin, index) => { let ipin = 'i' + pin opts[ipin] = opts[pin] || Object.assign({},opts.interrupt) || {} if (index === 1) opts[ipin].mport = opts[ipin].mport || 'B' delete opts[pin] if (opts[ipin].host) { opts[ipin].port = opts[ipin].port ? opts[ipin].port + +pin : 9000 + parseInt(pin) opts[ipin].host = opts[ipin].host opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + ipin + '#c>t' } // no host will make a pipe by default else { opts[ipin].path = (opts[ipin].path || 'interrupt') + ':'+ pin opts.sockets = (opts.sockets ? opts.sockets + ',' : '') + ipin + '#c>n' } }) } super(opts) log = logger({ file: 'src/mcp230xxi.js', class: 'MCP230XXi', name: 'mcp-interrupt', id: this.id }) log.info({ opts: opts, msg:'mcp interrupt options after calling super() on base'}) pins.forEach(pin => { this[pin] = opts['i' + pin] || {} }) this.pins = pins Object.assign(this.commands.pin, this.bindFuncs(ipincommands)) // add interrupt pin commands to base set in "command.js" this.addNamespace('commands', 'c') this._interruptProcess = null this.ready = false log.info({ opts: opts, pins: pins }, 'options for mcp interrupt processor') } async init() { await super.init() for (let pin of this.pins) { let cfg = { port: this[pin].mport || 'A', pins: this[pin].pins || 'all', cfg: this[pin].type || 'toggle_switch' } // this will set default type to internal pullup, only need to to change indivial pins to external if desired await this.pin.cfg(cfg) log.info('initial resetting of mcp interrupt port for corresponding sbc gpio pin') await this._reset(this[pin].mport) this.ready = true } this.on('interrupt', function(details) { details.id = this.id if (!this._interruptProcess) { console.log('----default interrupt processor for mcp instance----') console.log('create your own function and register it with .interruptProcess(function)') console.log(this.id) console.dir(details) } else this._interruptProcess(details) }) } async _reset(port) { // local non-packet hidden command log.debug(`resetting interrupt for port ${port || 'A'},${this.id} arg ${port !== 'B' ? 0x08 : 0x18}`) return await this.bus.read(port !== 'B' ? 0x08 : 0x18) // 0x08 is intcap interrupt capture register } interruptProcessor(func) { this._interruptProcess = func } } // end of MCP230XXi Class export default MCP230XXi // commands to be added to pin packet command functions const ipincommands = { interrupt: { reset: async function(packet) { log.info( { packet: packet }, `'socket has push requested a reset of mcp interrupt port for gpio pin ${ packet.pin }` ) let port = this[packet.pin].mport || 'A' await this._reset(port) }, // given a gpio interrupt then push a packet with cmd: 'pin.interrupt.find' and pin: the gpio pin number find: async function(inter) { // inter is a UCI packet if (this.ready) { // protects tripped interrupt before it's fully initialized, or interrupt requests arriving before porcessing is complete this.ready = false log.debug({msg:'raw packet from interrupt, will now find the pin that caused interrupt', inter:inter}) let packet = { pins: 'all', reg: 'intf' } packet.port = inter.port || this[inter.pin].mport || 'A' let res = await this.pin.status(packet) if (!res.status) return { error: 'no pin associated with interrupt' } let pin = byteFormat(res.status.port, { in: 'ARY', out: 'PLC' }) res.pin = pin[0] packet.pins = pin[0] packet.reg = null if (packet.pins) { // avoid bad interrupt (i.e. no pin caused interrupt) res.state = (await this.pin.status(packet)).status.pins[0][1] res.port = packet.port res.count = inter.count res.inter = inter.pin delete res.status this.emit('interrupt', res) log.debug({msg:'found pin now resetting mcp port interrupt', response:res}) } await this._reset(packet.port) this.ready = true return res } } } }