uci-mcp/src/mcp230xxi.js

161 lines
5.9 KiB
JavaScript

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 || 'input_interrupt'
}
// 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) {
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.ipin = inter.pin
res.port = packet.port
delete inter.cmd; delete inter._header; delete inter.pin
Object.assign(res,inter)
delete res.status
res.state = (await this.pin.status(packet)).status.pins[0][1]
console.log('emitted interrupt packet', res)
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
}
}
}
}