uci-mcp/src/mcp230xxi.js

192 lines
7.6 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 options doesn't contain a pin number key with mport and other interrupt pin related information
// then it is assume that pins[0] is port A and pins[1] is port B
// 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)
this.opts = 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
this.commands.interrupt = this.bindFuncs(icommands) // add interrupt pin commands to base set in "command.js"
this.addNamespace('commands', 'c') // add access via push to same commands
this._interruptProcess = process
this.ready = false
log.info({ opts: opts, pins: pins }, 'options for mcp interrupt processor')
}
async init() {
if (this.ipath) await this.addSocket('inter-n','s','n',{path:this.ipath})
if (this.iport) await this.addSocket('inter-t','s','t',{port:this.iport})
await super.init()
// this will set default type to internal pullup, only need to to change indivial pins to external if desired
await this.pin.cfg({port:'A',pins:'all',cfg:'input_interrupt'})
let status = await this._resetInterrupt('A')
log.debug('configure all port A pins as interrupts. Reset port A interrupt', status)
if (this.chip17) {
await this.pin.cfg({port:'B',pins:'all',cfg:'input_interrupt'})
let status = await this._resetInterrupt('B')
log.debug('configure all port B pins as interrupts. Reset port B interrupt', status)
}
}
async _readyInterrupt(port) {
let istate = await this.bus.read(port !== 'B' ? 0x07 : 0x17)
if (istate.response) {
this.ready = false
return false
}
else {
this.ready = true
return true
}
}
async _resetInterrupt(port) {
log.debug(`resetting interrupt for port ${port || 'A'},${this.id} arg ${port !== 'B' ? 0x08 : 0x18}`)
await this.bus.read(port !== 'B' ? 0x08 : 0x18) // 0x08 is intcap interrupt capture register
return(await this._readyInterrupt(port))
}
_getPortByPin(pin) {
let port = this[pin] ? this[pin].mport : null
let index = this.pins.map(pin => pin.toString()).indexOf(pin.toString())
port = port || index <= 0 ? 'A' : 'B'
return port
}
interruptProcessor(func) {
this._interruptProcess = func
}
} // end of MCP230XXi Class
export default MCP230XXi
// default processor
function process(details) {
details.id = this.id
console.log('----default interrupt processor for mcp instance----')
console.log('here is where you could either locally take some action for send/push on a message to another process')
console.log('create your own function and register it with .interruptProcesser(function)')
console.log(this.id)
console.dir(details)
}
// commands to be added to pin packet command functions
const icommands = {
status: async function(packet) {
// pin is interrupt pin on sbc for port
let port = packet.port || this._getPortByPin(packet.pin)
let res = await this._readyInterrupt(port)
return {cmd:'reply', request:'interrupt.status', port:port, ipin:packet.pin, ready:res}
},
reset: async function(packet) {
// pin is interrupt pin on sbc for port
let port = packet.port || this._getPortByPin(packet.pin)
log.info({ packet: packet, port:port, msg:`remote interrupt reset for port ${port}`})
await this._resetInterrupt(port)
let res = await this._readyInterrupt(port)
return {cmd:'reply', request:'interrupt.reset', port:port, ipin:packet.pin, ready:res}
},
// 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, finding pin that caused interrupt', inter:inter})
let packet = { pins: 'all', reg: 'intf' }
packet.port = inter.port || this._getPortByPin(inter.pin)
let res = await this.pin.status(packet) // read port interrupt status
let status = await this._resetInterrupt(packet.port)
log.debug({interrupt:res, reset:status, msg:'interrupt read and reset'})
this.ready = true
if (!res.status) {
log.warn({inter:inter, msg:'no pin associated with interrupt'})
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]
log.debug('emit/call interrupt details processing', res)
this.emit('mcpInterrupt',Object.assign({},res)) // emit for end user purposes, don't allow mutation
this._interruptProcess(res)
return res
} else {
log.warn({inter:inter, msg:'no pin associated with interrupt'})
return { error: 'no pin associated with interrupt' }
}
}
}
}